Conversation
Two changes so strong typecheck resolves define_method (and any other Module method) inside Class.new blocks: 1. Core fill: Class#new gets @yieldreceiver [::Class]. RBS records the binding only on Class#initialize ([self: Class], rbs >= 4.1); Class#new, the method actually resolved for Class.new-with-a-block, is (*untyped, **untyped) -> untyped in every RBS version, so a translator fix for [self: ...] would not reach this call site. 2. Pin::Block#rebind/#binder cascade: a block with no rebind of its own inherits its enclosing block's rebound binder instead of falling back to its statically-parsed context. Matches Ruby semantics (a block does not change self) and makes the existing class_eval/instance_eval fills work below one level of block nesting. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Aug 17, 2026
…er inheritance # Conflicts: # spec/type_checker/levels/strong_spec.rb
A receiverless call is resolved only against the binder's namespace, so
top-level helpers went unresolved wherever the binder was not itself the
root namespace - from an ordinary instance method, and (once a block
inherits an enclosing rebound binder) inside rebound blocks:
Object.define_method(:foo) do
some_top_level_helper # Unresolved call to some_top_level_helper
end
Top-level defs are pinned in the root namespace, not under Object, so no
ancestry walk reaches them. At runtime they are private instance methods
of Object and a receiverless call reaches them whatever self is, verified
against Ruby. Looking up '' reuses the root-context lookup
ApiMap#get_methods already implements for that namespace, so this adds no
new resolution rule.
Restricted to head? - no explicit receiver - because these methods are
private: 'foo'.top_level_helper raises NoMethodError, and that stays
unresolved. Foo.class_eval { bar } with bar an instance method of Foo
also stays unresolved, matching Ruby's NameError.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT
Positive: an ordinary instance method body and a class_eval block body both reach a top-level def, and the binder and a local variable each win over a top-level method of the same name. Negative: a top-level def is a private instance method of Object at runtime, so 'str'.helper raises NoMethodError and must stay unresolved. Verified locally that the positives fail with the top_level_pins fallback removed, and the negative fails if root-namespace methods are merged into Object's instance methods in ApiMap instead of guarding on head?. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT
Conversions#calculate_method_visibility received the scope it was computing a pin for but ignored it when handling a module function, marking both the instance and the singleton copy private. At runtime module_function makes only the instance copy private; the singleton copy is public, and is how these are normally called. The effect was that Marshal.dump, Signal.trap, FileUtils.rm_rf and JSON.parse all resolved to private pins, so any caller that asked for public methods - completion behind an explicit receiver, for one - could not see them. The YARD mapper already applied this rule; see YardMap::Mapper::ToMethod.make, which splits module_function into a private instance pin and a public class pin. This brings the RBS converter in line with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT
Replaces the head?-guarded top_level_pins fallback in Chain::Call with two changes that together express Ruby's actual rule. ApiMap#inner_get_methods now merges the root namespace's instance methods into Object's, but only when the request admits :private - the same pattern already used one method down for Kernel. Methods defined at the top level are private instance methods of Object at runtime, so this is where they belong. Chain::Call#resolve now asks for a visibility that depends on the call site instead of always taking get_method_stack's private-inclusive default. A receiverless call (head?) reaches private and protected methods; a call with an explicit receiver reaches them only from inside the receiver's namespace or a subclass, mirroring ApiMap#get_complex_type_methods. That keeps 'str'.top_level_helper unresolved without a special case in the chain, and also stops explicit-receiver calls from resolving unrelated classes' private methods. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT
apiology
commented
Aug 24, 2026
Names what the two lines actually do (force an enclosing block own @Rebind memo to populate before binder/context fall through to it) instead of leaving them inline under a comment.
"Memo" was unexplained jargon. State plainly that it forces the enclosing block's cached rebind to compute first, since this block's own binder/rebind fall through to it when unset.
Parallels the sibling maybe_rebind: both are conditional actions guarded on a runtime check, not just cache bookkeeping. The docstring now carries the why (binder/context fall through to the closure when unset) instead of the name restating the mechanism.
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Aug 27, 2026
Top-level method resolution in receiverless calls (including from inside Class.new-block/define_method contexts with a rebound self), gated on call-site visibility via a new visibility_for helper. Resolved a conflict in lib/solargraph/source/chain/call.rb: the incoming branch predates this repo's intersection-aware method_stack_pins/method_pins_for_binder refactor. Ported visibility_for through that newer structure instead of restoring the old inline dispatch loop - threaded name_pin through both helpers so the non-intersection get_method_stack call can compute visibility the same way the top-level pin_groups loop already did.
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Aug 27, 2026
ComplexType#intersect_with calls intersection_type.select when no type conforms. ComplexType defines #select; UniqueType does not, so a bare UniqueType (a value the method's own signature allows) fell through to Kernel#select, which is private, raising NoMethodError. castwide#1310's visibility_for fix stopped that call from silently no-oping and turned it into a real error. Normalize with #to_a (defined on both classes) before calling #select, matching the existing ComplexType-or-UniqueType idiom used elsewhere in this codebase.
# Conflicts: # lib/solargraph/source/chain/call.rb
The Kernel-specific fallback in calculate_method_visibility checked Kernel.private_method_defined? without checking which scope it was computing a pin for, so it marked the public singleton copy of a Kernel module function private too - the same bug the module_function check right below it already guards against with scope == :instance. Kernel.loop and other Kernel module functions called with an explicit receiver stopped resolving once Chain::Call#resolve began filtering by visibility for explicit-receiver calls, surfacing a pin visibility that was already wrong but previously went unfiltered. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Evcn8ABJk8ZhNmiDxJ28Th
The Kernel-specific fallback in ToMethod.make checked Kernel.private_method_defined? without checking which scope it was computing a pin for, marking the public singleton copy of a Kernel module function private too - the same class of bug 9a309f4 already fixed on the RBS-conversion side (rbs_map/conversions.rb), guarded there by scope == :instance the same way the module_function? check right below this one already guards on this file. Confirmed directly: building pins for Kernel.loop via YardMap::Mapper::ToMethod.make returned :private for both the instance and singleton copies before this change; only the singleton copy should be private, matching real Ruby's own visibility (Kernel#loop is private, Kernel.loop is public). Added a regression spec mirroring the RBS-side one, covering both the instance and singleton copies directly against ToMethod.make.
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Aug 29, 2026
Includes the Kernel module-function visibility fix on both the RBS (conversions.rb) and YARD (to_method.rb) pin-generation paths -- a public singleton copy of a Kernel module function was being marked private too, because the private-method fallback check did not guard on scope the way the module_function check right below it already did. Conflict in chain/call.rb: kept HEAD's side in all three hunks -- this branch's own visibility_for/union-arm-resolution logic (from the earlier castwide#1310 merge plus later castwide#1231/castwide#1314 work) already supersedes what this incoming diff carries for that file.
Cut the visibility_for docstring to 2 lines and the top-level private-method comment in inner_get_methods to 1 line, per PR review.
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Sep 1, 2026
Two comments this PR added ran over the 1-3 line budget or narrated an external reference instead of leaving it bare. - api_map.rb: compressed the 5-line explanation of why Class#new's return type is widened to Object down to 2 lines, keeping only the RBS constraint and the resulting behavior. - strong_spec.rb: reworded the define_method-is-unresolved comment and replaced the narrated "castwide#1310's territory" reference with the bare PR URL (castwide#1310).
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Sep 5, 2026
Resolve every conflict by where `ours` came from, not by comparing the two sides on their merits. Overcommit 0.71.0 destroyed merge state on this branch repeatedly, so several deliberate castwide#1231 changes present as divergent local work. Where `ours` is castwide#1231 code that castwide#1231 itself later changed, take theirs: the `closure` parameter (derivable as `name_pin.closure`), the inlined `parse_type_string`, `Signature#key_param_index`, the four `*_complex_type` object-graph builders, and `key_verified_conjuncts`, now `argument_verified_conjuncts`. Where `ours` is independent work, port it onto castwide#1231's structure and signatures rather than the reverse: `receiver_path`/`narrowed_call_pin`, per-arm `self_binder` resolution, keyword-argument matching and `require_literal`, the yielded-parameter signature preference, the duck-type and bot branches, castwide#1310's `visibility_for`, and castwide#1255's RBS type-alias expansion, which now threads through `type_to_tag`. Auto-merge silently dropped `Intersection#intersection_tag` and `ComplexType::QUOTE_CHARACTERS`; both are restored. castwide#1231's `RBS_INTERFACE_TO_GENERIC` stub stays out - it stands in for structural interface types, which castwide#1266 supplies here for real. Its `pull/1223` pending guards go too, since castwide#1223 is merged on this branch.
apiology
marked this pull request as ready for review
September 5, 2026 22:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Solargraph's strong typecheck reports
Unresolved callfor methods that work at runtime inside aClass.newblock:Two changes:
Class#newgets@yieldreceiver [::Class]. RBS binds onlyClass#initialize([self: Class], rbs >= 4.1);Class#newis(*untyped, **untyped) -> untypedin every version, so a[self: ...]translator fix never reaches this call site.@yieldreceivercan't express the superclass parameter, so the fill stays unparameterized.Pin::Block#rebind/#bindercascade: a block with no rebind of its own inherits its enclosing block's binder. Without it, every existing fill (class_eval, ...) stops one nesting level down.Three new strong-level specs (direct,
.each-nested,class_eval-nested).🤖 This PR was written by Claude (Claude Code), operated by @apiology.