Resolve self, instance, and class from the receiver in runtime type checks - #3194
Merged
Merged
Conversation
…type checks `RBS::Test::TypeCheck` took the classes for `self`, `instance`, and `class` as three independent arguments, and each caller filled them in differently. `assert_send_type` took `instance` from the `testing` target, and `rbs test` checked `self` against the hooked class and left `instance` as `Object`. So a method called on a subclass couldn't be checked against `instance` or `self`, and `rbs test` didn't check `instance` at all. At runtime the three types are determined by the receiver's class and whether the call is an instance or a singleton method call: `instance` is the class, `class` is its singleton class, and `self` is one of them. Model that as `TypeCheck::InstanceContext` and `TypeCheck::SingletonContext`, built from the receiver with `.of`, and pass one of them as `context:`. Both `rbs test` and the unit test assertions build the context from the actual receiver. Without a context, checking `self`, `instance`, or `class` raises `TypeCheck::NoReceiverContextError`, which replaces the placeholder strings `assert_type` used to pass. The `self_class:`, `instance_class:`, and `class_class:` keywords still work but emit a deprecation warning. Fixes #3192 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review fixes for the receiver context: - A module's instance method hook also runs when the module is used via `extend`, where the receiver is a class. Building an `InstanceContext` from it made `instance` mean `Class`, so `Widget.build` with `def build: () -> instance` was reported as an error. When the receiver is a module whose class doesn't include the hooked module, use a `SingletonContext` instead, as Steep does. - Errors were tagged with the receiver's class, which loses the module that declares the method (`[C#foo]` instead of `[M#foo]`) and drops the name of anonymous subclasses. Add `owner:` to `TypeCheck.new` for the subject of the errors. `rbs test` passes the hooked class, so the tags are the same as before. - Passing `context:` together with the deprecated keywords now raises `ArgumentError` instead of ignoring them. The deprecation warning is shown without the `:deprecated` category, like the other deprecation in this gem, and only once per process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
soutaro
approved these changes
Oct 5, 2026
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.
Fixes #3192.
RBS::Test::TypeCheckreceived the classes forself,instance, andclassas three separate arguments, and its callers filled them in inconsistently. As a result, a method called on a subclass could not be checked precisely, andrbs testdid not checkinstanceat all.At runtime, all three are determined by the receiver's class and whether the call is an instance or a singleton method call. This PR models that as a single value:
selfinstanceclassInstanceContext.of(obj)obj.classobj.classobj.class.singleton_classSingletonContext.of(mod)mod.singleton_classmodmod.singleton_classBoth
rbs testandRBS::UnitTest::TypeAssertionsnow build the context from the actual receiver. Inrbs test, a module's instance method called on a class that extends the module is checked as a singleton method call, as Steep does.The subject of the errors is given separately as
owner:.rbs testpasses the hooked class, so error tags stay the same as before. With the reproduction from the issue,rbs testnow reports all four violations:Compatibility
self_class:,instance_class:, andclass_class:are still accepted, with a deprecation warning shown once per process. Passing them together withcontext:raisesArgumentError.self,instance, orclassraisesTypeCheck::NoReceiverContextError.assert_typeandassert_const_typeuse this instead of placeholder values.TypeAssertions#instance_classand#class_classare removed.🤖 Generated with Claude Code