Skip to content

Commit c864496

Browse files
DinoVmeta-codesync[bot]
authored andcommitted
Add support for resolving called functions
Summary: We now have `defining_func` coming out, let's start using it. Adds test cases for a user-defined and built-in function. Reviewed By: yoney Differential Revision: D96235001 fbshipit-source-id: f345b95f4c76e3e3a677eb3457e416db9de50880
1 parent 86c5f7a commit c864496

5 files changed

Lines changed: 70 additions & 0 deletions

File tree

cinderx/PythonLib/cinderx/compiler/static/pyrefly_info.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ def lookup(
136136
node.value,
137137
member,
138138
)
139+
elif entry["kind"] == "callable" and "defining_func" in entry:
140+
defining_func_qname = str(entry["defining_func"])
141+
resolved_func = self.resolve_func(defining_func_qname, modules, type_env)
142+
if resolved_func is not None:
143+
return resolved_func
139144

140145
# Fallback to types
141146
return self.lookup_type(type_index, modules, type_env)
@@ -169,6 +174,42 @@ def load_json(cls, json_path: str) -> PyreflyTypeInfo:
169174
data = json.load(f)
170175
return cls(data)
171176

177+
def resolve_func(
178+
self,
179+
qname: str,
180+
modules: dict[str, ModuleTable],
181+
type_env: TypeEnvironment,
182+
) -> Value | None:
183+
"""Resolve a dotted qname like 'module.path.func_name' to a callable Value.
184+
185+
Splits the qname on '.' and tries progressively shorter prefixes
186+
as module names, then looks up the function name in the module.
187+
This handles both user-defined Functions and built-in callable
188+
types like LenFunction.
189+
"""
190+
parts = qname.split(".")
191+
192+
for i in range(len(parts) - 1, 0, -1):
193+
mod_name = ".".join(parts[:i])
194+
if mod_name in modules:
195+
mod = modules[mod_name]
196+
result = mod.get_child(parts[i], mod_name)
197+
if result is None:
198+
continue
199+
# Walk remaining parts (e.g. Class.method)
200+
for part in parts[i + 1 :]:
201+
if isinstance(result, Class):
202+
result = result.get_member(part)
203+
else:
204+
result = None
205+
break
206+
if result is None:
207+
break
208+
if result is not None:
209+
return result
210+
211+
return None
212+
172213
def resolve_classname(
173214
self, qname: str, modules: dict[str, ModuleTable], type_env: TypeEnvironment
174215
) -> Class | None:
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def f() -> None:
2+
pass
3+
4+
5+
def g() -> None:
6+
return f()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from types import CodeType
2+
3+
from test_cinderx.test_compiler.test_static.pyrefly_binder import PyreBinderTests
4+
5+
6+
def verify(test: PyreBinderTests, code: CodeType) -> None:
7+
"""Verify that the compiled code includes INVOKE_METHOD."""
8+
9+
g = test.find_code(code, "g")
10+
test.assertInBytecode(g, "INVOKE_FUNCTION")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def f() -> int:
2+
x = {}
3+
return len(x)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from types import CodeType
2+
3+
from test_cinderx.test_compiler.test_static.pyrefly_binder import PyreBinderTests
4+
5+
6+
def verify(test: PyreBinderTests, code: CodeType) -> None:
7+
"""Verify that the compiled code includes FAST_LEN."""
8+
9+
f = test.find_code(code, "f")
10+
test.assertInBytecode(f, "FAST_LEN")

0 commit comments

Comments
 (0)