fix(lsp): 包的 __init__ 重导出成员可解析

A 是包时 A/__init__.vp 里的 from .leaf import b 被模块链直接跳过:_ChainFromModule 看到 __init__.vp 就切到包目录,只找子模块。改为先在 __init__ 的模块作用域解析该名字(含 from-import 重导出),未命中再下钻子模块。gate10 增加包重导出 token/跳转断言。
This commit is contained in:
TermiNexus committed 2026-10-27 17:43:57 +08:00
1 parent 4e3331d442
commit b81e4ae3bf
5 files changed
+35 -8

No files matched your search

+11 -8
View File
@@ -874,19 +874,22 @@ def _ChainFromModule(ws: LspWorkspace.LspWorkspace*, pool: memhub.MemManager*,
hops: int) -> LspTarget*:
segs: str = rest
while segs is not None:
if cur_is_file != 0:
base: str = LspFiles.BaseName(pool, cur)
if base is not None and string.strcmp(base, "__init__.vp") == 0:
# A package's __init__.vp is not a symbol table for its submodules; descend from
# the package directory for the remaining dotted segments.
cur = LspFiles.DirName(pool, cur)
cur_is_file = 0
continue
h: str = None
r: str = None
LspSymbols._SplitHead(pool, segs, &h, &r)
if h is None:
return None
if cur_is_file != 0:
base: str = LspFiles.BaseName(pool, cur)
if base is not None and string.strcmp(base, "__init__.vp") == 0:
# A package's __init__.vp can re-export members (`from .leaf import X`).
# Try those first; only descend into the package directory when the name is
# not a member (then it may be a submodule/package).
pkg_member: LspSymbols.LspSymbol* = ResolveInFile(pool, table_pool, cur, None, h)
if pkg_member is None:
cur = LspFiles.DirName(pool, cur)
cur_is_file = 0
continue
if cur_is_file != 0:
member: LspSymbols.LspSymbol* = ResolveInFile(pool, table_pool, cur, None, h)
if member is None:
@@ -1 +1,2 @@
"""Gate 10 dotted-import package."""
from .leaf import reexport_fn
+5
View File
@@ -0,0 +1,5 @@
"""Gate 10 package re-export leaf module."""
def reexport_fn(x: int) -> int:
return x
+5
View File
@@ -4,6 +4,7 @@ from .gate10_types import Widget, DOUBLE
from .gate10_stub import StubThing
from .gate10_extra import *
import gate10_types
import gate10_pkg
import gate10_pkg.sub.mod
import gate10_pkg.sub as pkgsub
import gate10_pkg.sub as sub
@@ -39,6 +40,10 @@ def stub_chain() -> int:
return StubThing.count
def reexport_use() -> int:
return gate10_pkg.reexport_fn(1)
def else_block(flag: int) -> int:
if flag != 0:
return 1
+13
View File
@@ -1522,6 +1522,19 @@ def gate10(exe: str) -> int:
raise AssertionError(f"missing definition in {label}: {else_def}")
if not else_def[0]["uri"].replace("\\", "/").endswith("gate10_extra.vp"):
raise AssertionError(f"wrong definition in {label}: {else_def}")
re_pos = _position(text, "gate10_pkg.reexport_fn")
re_pos["character"] += len("gate10_pkg.")
if not _find_token(sem_tokens, re_pos["line"], re_pos["character"], sem_types.index("function")):
raise AssertionError(f"package re-export member is not highlighted: {re_pos}")
re_def = client.request(
"textDocument/definition",
{"textDocument": {"uri": uri}, "position": re_pos},
timeout=120.0,
).get("result")
if not isinstance(re_def, list) or not re_def:
raise AssertionError(f"package re-export definition failed: {re_def}")
if not re_def[0]["uri"].replace("\\", "/").endswith("gate10_pkg/leaf.vp"):
raise AssertionError(f"package re-export definition is wrong: {re_def}")
const_pos = _position(text, "EXTRA_CONST")
if not _find_token_mods(sem_tokens, const_pos["line"], const_pos["character"],
sem_types.index("variable"),