Skip to content

Re-opened classes should not reset superclass to 'Object' - #1315

Open
apiology wants to merge 1 commit into
castwide:masterfrom
apiology:task28-yard-implicit-superclass
Open

Re-opened classes should not reset superclass to 'Object'#1315
apiology wants to merge 1 commit into
castwide:masterfrom
apiology:task28-yard-implicit-superclass

Conversation

@apiology

Copy link
Copy Markdown
Contributor

With activesupport in the bundle, Solargraph reports the wrong superclass for DateTime:

DateTime.superclass                # => Date, at runtime
get_superclass('DateTime')         # => Object

DateTime is then cut off from Date and from everything above it.

The Object comes from activesupport reopening the class:

# active_support/core_ext/date_time/blank.rb
class DateTime # :nodoc:
  def blank?
    false
  end
end

A reopening with no superclass clause asserts nothing about ancestry — and it cannot, because Ruby fixes a class's superclass at its first definition and refuses to let anything restate it differently:

class DateTime < Object; end       # TypeError: superclass mismatch for class DateTime

A class with no superclass reference already resolves to Object via Store#try_special_superclasses, so the mapper now skips recording an Object reference at all, leaving only the specific one that carries a claim. get_superclass is untouched — it stays a plain .first with no tie-break, because there is no longer a conflict to arbitrate. BasicObject references are kept, since the only class YARD defaults to BasicObject is Object itself, where the reference is correct either way.

Suite: 1625 examples, 0 failures, 60 pending. Opening as a draft.

This PR was written by Claude (Anthropic's Claude Code) on behalf of @apiology.

🤖 Generated with Claude Code

https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT

YARD fills in `Object` as the superclass of every class it never saw a
superclass clause on, including a bare `class Foo` reopening, and keeps
no record of which case it was. These three produce byte-identical
`P(Object)` proxies, and that survives the yardoc round-trip:

    class Reopened; def a; end; end   # reopening, asserts nothing
    class Fresh; end                  # genuinely Object
    class Explicit < Object; end      # declared

Store#get_superclass takes the first recorded reference, so a gem
reopening a class could shadow the real superclass declared elsewhere.
With activesupport in the bundle
(`active_support/core_ext/date_time/blank.rb` reopens `class DateTime`),
`get_superclass('DateTime')` returned `Object` instead of RBS's `Date`,
so `Comparable` was never reached:

    get_method_stack('Date', '<')      # => [Comparable#<]
    get_method_stack('DateTime', '<')  # => []

which reports `Unresolved call to <` for `some_date_time < other` and,
for a `Date, DateTime` union receiver,
`Unresolved call to > on Date, DateTime`.

A class with no superclass reference already resolves to Object through
Store#try_special_superclasses, so dropping these references at the
mapper loses nothing and leaves get_superclass with no conflict to
arbitrate.

`BasicObject` references are kept. The only class YARD defaults to a
BasicObject superclass is Object itself, whose superclass genuinely is
BasicObject, so the recorded reference is correct whether it came from a
default or from an explicit `< BasicObject`.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01H1FEjW6nMpZrWPmeWX9miT
@apiology
apiology marked this pull request as ready for review August 18, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant