mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-13 15:40:05 +00:00
fix: preserve Ruby constant lookup boundaries
This commit is contained in:
1 parent
1a7262e2a8
commit
bd1a2d2651
3 files changed
+64
-7
No files matched your search
@@ -3159,15 +3159,18 @@ def _ruby_external_method_owners(root_node, source: bytes) -> list[str]:
|
||||
refs.update(constant_refs(child))
|
||||
return refs
|
||||
|
||||
def alias_rhs_refs(node) -> set[str]:
|
||||
def alias_rhs_refs(node, *, allow_array: bool = False) -> set[str]:
|
||||
if node.type in {"constant", "scope_resolution"}:
|
||||
raw = _ruby_const_full_name(node, source)
|
||||
return {raw} if raw else set()
|
||||
if node.type in {"parenthesized_statements", "right_assignment_list"}:
|
||||
if node.type in {
|
||||
"parenthesized_statements",
|
||||
"right_assignment_list",
|
||||
} or (allow_array and node.type == "array"):
|
||||
refs: set[str] = set()
|
||||
for child in node.children:
|
||||
if child.is_named:
|
||||
refs.update(alias_rhs_refs(child))
|
||||
refs.update(alias_rhs_refs(child, allow_array=allow_array))
|
||||
return refs
|
||||
return set()
|
||||
|
||||
@@ -3189,7 +3192,12 @@ def _ruby_external_method_owners(root_node, source: bytes) -> list[str]:
|
||||
# too. Only direct/parenthesized constant RHS forms qualify;
|
||||
# ordinary values containing constant references do not.
|
||||
owners.update(
|
||||
prefix_marker(raw) for raw in alias_rhs_refs(right)
|
||||
prefix_marker(raw)
|
||||
for raw in alias_rhs_refs(
|
||||
right,
|
||||
allow_array=left is not None
|
||||
and left.type == "left_assignment_list",
|
||||
)
|
||||
)
|
||||
if node.type in {"class", "module"}:
|
||||
name = node.child_by_field_name("name")
|
||||
|
||||
@@ -224,13 +224,13 @@ def resolve_ruby_member_calls(
|
||||
if len(candidates) > 1:
|
||||
return None
|
||||
if scope and len(ref_parts) > 1:
|
||||
prefix_label = "::".join(
|
||||
first_prefix_label = "::".join(
|
||||
[
|
||||
*[part for part in scope.split("::") if part],
|
||||
*ref_parts[:-1],
|
||||
ref_parts[0],
|
||||
]
|
||||
)
|
||||
if class_labels.get(prefix_label):
|
||||
if class_labels.get(first_prefix_label):
|
||||
# Ruby bound the qualified prefix in this nearer lexical
|
||||
# scope. Its inherited constants are not modelled, so do
|
||||
# not retry the same prefix in an outer scope.
|
||||
|
||||
@@ -812,6 +812,25 @@ def test_inherited_call_stays_inferred_for_multiple_constant_write(
|
||||
assert _only_call(graph)["confidence"] == "INFERRED"
|
||||
|
||||
|
||||
def test_destructured_constant_alias_owner_stays_inferred(tmp_path: Path) -> None:
|
||||
_write(tmp_path, "base.rb", "class Base\n def self.helper; :wrong; end\nend\n")
|
||||
_write(
|
||||
tmp_path,
|
||||
"child.rb",
|
||||
"class Child < Base\n def self.call\n helper()\n end\nend\n",
|
||||
)
|
||||
_write(
|
||||
tmp_path,
|
||||
"override.rb",
|
||||
"AliasChild, Spare = [Child, Object]\n"
|
||||
"AliasChild.define_singleton_method(:helper) { :child }\n",
|
||||
)
|
||||
|
||||
graph = extract(sorted(tmp_path.glob("*.rb")), cache_root=tmp_path, parallel=False)
|
||||
|
||||
assert _only_call(graph)["confidence"] == "INFERRED"
|
||||
|
||||
|
||||
def test_inherited_call_stays_inferred_under_enclosing_refinement(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
@@ -1059,6 +1078,36 @@ def test_qualified_superclass_stops_at_nearer_lexical_prefix(
|
||||
assert _only_call(graph)["confidence"] == "INFERRED"
|
||||
|
||||
|
||||
def test_deep_qualified_superclass_stops_at_first_lexical_segment(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
_write(
|
||||
tmp_path,
|
||||
"base.rb",
|
||||
"module Outer\n module Services\n module API\n class Base\n"
|
||||
" def self.helper; :wrong; end\n end\n end\n end\nend\n",
|
||||
)
|
||||
_write(
|
||||
tmp_path,
|
||||
"provider.rb",
|
||||
"class Provider\n module API\n class Base\n class << self\n"
|
||||
" attr_reader :helper\n end\n end\n end\nend\n",
|
||||
)
|
||||
_write(
|
||||
tmp_path,
|
||||
"child.rb",
|
||||
"module Outer\n module Inner\n"
|
||||
" class Services < ::Provider; end\n"
|
||||
" class Child < Services::API::Base\n"
|
||||
" def self.call\n helper()\n end\n end\n"
|
||||
" end\nend\n",
|
||||
)
|
||||
|
||||
graph = extract(sorted(tmp_path.glob("*.rb")), cache_root=tmp_path, parallel=False)
|
||||
|
||||
assert _only_call(graph)["confidence"] == "INFERRED"
|
||||
|
||||
|
||||
def test_inherited_call_stops_at_ambiguous_nearer_method(tmp_path: Path) -> None:
|
||||
_write(tmp_path, "grand.rb", "class Grand\n def helper; :grand; end\nend\n")
|
||||
_write(
|
||||
|
||||
Reference in new issue
Block a user