Skip to content

Resolve retroactive singleton method visibility changes#781

Merged
alexcrocha merged 1 commit into
mainfrom
05-04-resolve_singleton_method_visibility
May 7, 2026
Merged

Resolve retroactive singleton method visibility changes#781
alexcrocha merged 1 commit into
mainfrom
05-04-resolve_singleton_method_visibility

Conversation

@alexcrocha
Copy link
Copy Markdown
Contributor

@alexcrocha alexcrocha commented May 5, 2026

Part of #89 and follows #780, which added the SINGLETON_METHOD_VISIBILITY bit flag and indexed singleton-flagged MethodVisibilityDefinitions.

This PR routes singleton-flagged defs through the singleton class and folds the singleton path into the existing resolve_method_visibilities pass. Visibility definitions are processed after the main convergence loop, so the target method declaration is guaranteed to exist by the time we attach visibility.

For inherited targets like private_class_method :foo where foo comes from a parent class's singleton, we create a child-owned MethodDeclaration on the child's singleton class. This matches what #738 did for instance methods, and what Ruby reports when you ask Child.singleton_class.instance_method(:foo).owner:

class Parent
  def self.foo; end
end

class Child < Parent
  private_class_method :foo
end

Child.singleton_class.instance_method(:foo).owner
# => #<Class:Child>   (Ruby copies the method to Child when visibility changes)

Parent.singleton_class.instance_method(:foo).owner
# => #<Class:Parent>  (untouched on the parent)

Why document-scoped diagnostics for the singleton path

When a singleton target doesn't resolve, the diagnostic attaches to the document, not to the singleton declaration. Walking ancestors via get_or_create_singleton_class(Foo, ...) can synthesize Foo::<Foo> as a side effect, even when it has no real members. For class Foo; private_class_method :missing; end where Foo has no other singleton methods, that synthetic singleton class only exists to host the diagnostic. Attaching to it would leak the declaration on file delete. Document-scoped clears with the file. Instance-method diagnostics keep their existing declaration scope: their owner can't be synthetic.

Source-order processing of visibility defs

Ruby applies visibility statements in the order they appear in source, so the later one wins:

class Foo
  def bar; end
  private :bar
  public :bar          # Ruby: bar is Public
end

This was already broken on main for instance methods; surfaced while wiring up the singleton path, fixed here for both. prepare_units sorted definitions and const_refs by (uri_id, offset) for determinism but left others (which holds the visibility defs queued by handle_remaining_definitions) in IdentityHashMap iteration order, which is bucket-hash order. So Foo#bar could end up Private because the resolver happened to apply public first and private second. Same sort applied to others so the override-order invariant holds for both instance and singleton paths.

In this PR

  • branch on is_singleton_method_visibility() inside resolve_method_visibilities: resolve through get_or_create_singleton_class for the singleton path, render "class method" vs "method" in the diagnostic, attach document-scoped (singleton) vs declaration-scoped (instance)
  • add Graph::find_singleton_method_visibility_declaration and route through it from the existing Definition::MethodVisibility arm in definition_to_declaration_id when the flag is set
  • sort others by (uri_id, offset) in prepare_units so visibility overrides apply in source order

Copy link
Copy Markdown
Contributor Author

alexcrocha commented May 5, 2026

@alexcrocha alexcrocha changed the title Resolve singleton method visibility Resolve retroactive singleton method visibility changes May 5, 2026
@alexcrocha alexcrocha marked this pull request as ready for review May 5, 2026 22:39
@alexcrocha alexcrocha requested a review from a team as a code owner May 5, 2026 22:39
@alexcrocha alexcrocha self-assigned this May 5, 2026
@alexcrocha alexcrocha added the enhancement New feature or request label May 5, 2026
@alexcrocha alexcrocha changed the base branch from 05-04-index_private_class_method___public_class_method_calls to graphite-base/781 May 6, 2026 18:20
@alexcrocha alexcrocha force-pushed the 05-04-resolve_singleton_method_visibility branch from 299b305 to 0196acc Compare May 7, 2026 04:11
@alexcrocha alexcrocha changed the base branch from graphite-base/781 to 05-04-index_private_class_method___public_class_method_calls May 7, 2026 04:11
@alexcrocha alexcrocha force-pushed the 05-04-resolve_singleton_method_visibility branch from 0196acc to 4ee8994 Compare May 7, 2026 04:45
Comment thread rust/rubydex/src/resolution_tests.rs Outdated
Comment thread rust/rubydex/src/resolution_tests.rs
Comment thread rust/rubydex/src/resolution.rs Outdated
Comment thread rust/rubydex/src/resolution.rs Outdated
Comment thread rust/rubydex/src/resolution_tests.rs Outdated
@alexcrocha alexcrocha force-pushed the 05-04-resolve_singleton_method_visibility branch from 4ee8994 to e8bbe37 Compare May 7, 2026 18:15
@alexcrocha alexcrocha force-pushed the 05-04-index_private_class_method___public_class_method_calls branch from 16d134d to 27da4dd Compare May 7, 2026 18:15
@alexcrocha alexcrocha force-pushed the 05-04-resolve_singleton_method_visibility branch from e8bbe37 to dd97c9f Compare May 7, 2026 19:49
@alexcrocha alexcrocha force-pushed the 05-04-index_private_class_method___public_class_method_calls branch from 27da4dd to ce07ec6 Compare May 7, 2026 19:53
@alexcrocha alexcrocha force-pushed the 05-04-resolve_singleton_method_visibility branch from dd97c9f to c2e1a87 Compare May 7, 2026 19:53
@alexcrocha alexcrocha changed the base branch from 05-04-index_private_class_method___public_class_method_calls to graphite-base/781 May 7, 2026 19:57
@graphite-app graphite-app Bot changed the base branch from graphite-base/781 to main May 7, 2026 19:57
@alexcrocha alexcrocha force-pushed the 05-04-resolve_singleton_method_visibility branch from c2e1a87 to 7501d0a Compare May 7, 2026 19:57
@alexcrocha alexcrocha force-pushed the 05-04-resolve_singleton_method_visibility branch from 7501d0a to 94a1a2f Compare May 7, 2026 19:58
@alexcrocha alexcrocha merged commit a05abc5 into main May 7, 2026
36 checks passed
Copy link
Copy Markdown
Contributor Author

Merge activity

@alexcrocha alexcrocha deleted the 05-04-resolve_singleton_method_visibility branch May 7, 2026 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants