-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathresolver_lib.lua
More file actions
173 lines (153 loc) · 6.36 KB
/
resolver_lib.lua
File metadata and controls
173 lines (153 loc) · 6.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
-- PHP namespace resolution logic
-- Resolves short class names to fully-qualified names using use statements
-- Entry kind: library.lua
---------------------------------------------------------------------------
-- Built-in PHP types (never resolve via namespace)
---------------------------------------------------------------------------
local builtin_types = {
-- Scalar
["string"] = true, ["int"] = true, ["float"] = true, ["bool"] = true,
["array"] = true, ["object"] = true, ["callable"] = true, ["iterable"] = true,
["void"] = true, ["never"] = true, ["null"] = true, ["false"] = true,
["true"] = true, ["mixed"] = true, ["self"] = true, ["static"] = true,
["parent"] = true,
}
--- Check if a name is a PHP built-in type
local function is_builtin(name)
return builtin_types[string.lower(name)] == true
end
---------------------------------------------------------------------------
-- Import index builder
---------------------------------------------------------------------------
--- Build a lookup table from imports
-- @param imports {Import} Array of {fqn, alias}
-- @return table Maps short_name|alias → fqn
local function build_import_index(imports)
local index = {}
for _, imp in ipairs(imports) do
if imp.alias then
index[imp.alias] = imp.fqn
else
-- Extract short name from FQN: "App\Services\OrderService" → "OrderService"
local short = string.match(imp.fqn, "([^\\]+)$")
if short then
index[short] = imp.fqn
end
end
end
return index
end
---------------------------------------------------------------------------
-- Name resolution
---------------------------------------------------------------------------
--- Resolve a PHP class/type name to its fully-qualified name
-- Resolution order:
-- 1. If built-in type → return nil (not a class reference)
-- 2. If starts with \ → absolute FQN (strip leading \)
-- 3. If matches a use-statement short name or alias → return that FQN
-- 4. If contains \ (partially qualified) → prepend current namespace
-- 5. Otherwise → prepend current namespace
--
-- @param name string The name to resolve (e.g. "OrderService", "Carbon\Carbon")
-- @param import_index table Map of short_name → FQN from use statements
-- @param namespace string|nil Current file's namespace
-- @return string|nil Resolved FQN, or nil if built-in/unresolvable
local function resolve(name, import_index, namespace)
if not name or name == "" then return nil end
-- Trim whitespace
name = string.match(name, "^%s*(.-)%s*$")
-- Built-in types
if is_builtin(name) then return nil end
-- Absolute: \App\Foo → App\Foo
if string.sub(name, 1, 1) == "\\" then
return string.sub(name, 2)
end
-- Check use-statement imports
-- First try exact match (for short names like "OrderService")
if import_index[name] then
return import_index[name]
end
-- For qualified names like "Carbon\Carbon", check first segment
local first_segment = string.match(name, "^([^\\]+)")
if first_segment and import_index[first_segment] then
local rest = string.match(name, "^[^\\]+\\(.+)$")
if rest then
return import_index[first_segment] .. "\\" .. rest
end
return import_index[first_segment]
end
-- Prepend current namespace
if namespace and namespace ~= "" then
return namespace .. "\\" .. name
end
-- Global namespace
return name
end
--- Resolve all references in a file, setting to_resolved on each
-- @param references {Reference} Array of references
-- @param imports {Import} Array of {fqn, alias}
-- @param namespace string|nil Current file's namespace
local function resolve_references(references, imports, namespace)
local import_index = build_import_index(imports)
for _, ref in ipairs(references) do
local name = ref.to_name
-- For static_method_call like "OrderService::process", split and resolve class
if ref.kind == "static_method_call" then
local cls, method = string.match(name, "^(.+)::(.+)$")
if cls then
local resolved_cls = resolve(cls, import_index, namespace)
if resolved_cls then
ref.to_resolved = resolved_cls .. "::" .. method
end
end
elseif ref.kind == "method_call" then
-- Method calls on variables: we can't resolve the type from tree-sitter alone
-- Leave to_resolved as nil; later stages may use type info
-- But $this->method() can be resolved if we know the enclosing class
if ref.receiver == "$this" and ref.from_symbol then
local cls = string.match(ref.from_symbol, "^(.+)::")
if cls then
ref.to_resolved = cls .. "::" .. name
end
end
elseif ref.kind == "function_call" then
-- Built-in functions stay unresolved
-- User functions get namespace resolution
local resolved = resolve(name, import_index, namespace)
ref.to_resolved = resolved
else
-- Type references: instantiation, type_hint, extends, implements, etc.
local resolved = resolve(name, import_index, namespace)
ref.to_resolved = resolved
end
end
end
--- Resolve extends/implements/uses on symbols
-- @param symbols {Symbol}
-- @param imports {Import}
-- @param namespace string|nil
local function resolve_symbol_relations(symbols, imports, namespace)
local import_index = build_import_index(imports)
for _, sym in ipairs(symbols) do
if sym["extends"] then
sym["extends"] = resolve(sym["extends"], import_index, namespace) or sym["extends"]
end
if sym["implements"] then
for i, iface in ipairs(sym["implements"]) do
sym["implements"][i] = resolve(iface, import_index, namespace) or iface
end
end
if sym.uses then
for i, trait in ipairs(sym.uses) do
sym.uses[i] = resolve(trait, import_index, namespace) or trait
end
end
end
end
return {
resolve = resolve,
resolve_references = resolve_references,
resolve_symbol_relations = resolve_symbol_relations,
build_import_index = build_import_index,
is_builtin = is_builtin,
}