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
use std::collections::{HashMap, HashSet};
/// Known global symbols and their fields, populated from Lua VM
/// introspection or manual registration.
#[derive(Debug, Clone, Default)]
pub struct SymbolTable {
/// Top-level global names (e.g. `print`, `alc`, `table`).
globals: HashSet<String>,
/// First-level fields of global tables (e.g. `alc` → {`llm`, `state`}).
global_fields: HashMap<String, HashSet<String>>,
/// LuaCats `---@class` definitions: class name → known field names.
class_fields: HashMap<String, HashSet<String>>,
}
impl SymbolTable {
pub fn new() -> Self {
Self::default()
}
/// Register a top-level global name.
pub fn add_global(&mut self, name: &str) {
self.globals.insert(name.to_string());
}
/// Register a field of a global table.
pub fn add_global_field(&mut self, table: &str, field: &str) {
self.global_fields
.entry(table.to_string())
.or_default()
.insert(field.to_string());
}
/// Check whether a top-level global name is known.
pub fn has_global(&self, name: &str) -> bool {
self.globals.contains(name)
}
/// Check whether a field of a global table is known.
pub fn has_global_field(&self, table: &str, field: &str) -> bool {
self.global_fields
.get(table)
.is_some_and(|fields| fields.contains(field))
}
/// Get all known fields for a global table (for "did you mean?"
/// suggestions).
pub fn global_fields_for(&self, table: &str) -> Option<&HashSet<String>> {
self.global_fields.get(table)
}
/// Register a `---@class` definition (creates an empty field set if new).
pub fn add_class(&mut self, class_name: &str) {
self.class_fields.entry(class_name.to_string()).or_default();
}
/// Register a field on a `---@class`.
pub fn add_class_field(&mut self, class_name: &str, field: &str) {
self.class_fields
.entry(class_name.to_string())
.or_default()
.insert(field.to_string());
}
/// Check whether a class is known.
pub fn has_class(&self, class_name: &str) -> bool {
self.class_fields.contains_key(class_name)
}
/// Check whether a field exists on a class.
pub fn has_class_field(&self, class_name: &str, field: &str) -> bool {
self.class_fields
.get(class_name)
.is_some_and(|fields| fields.contains(field))
}
/// Get all known fields for a class (for "did you mean?" suggestions).
pub fn class_fields_for(&self, class_name: &str) -> Option<&HashSet<String>> {
self.class_fields.get(class_name)
}
/// Pre-populate with Lua 5.4 standard library globals.
pub fn with_lua54_stdlib(mut self) -> Self {
let stdlib_globals = [
"assert",
"collectgarbage",
"dofile",
"error",
"getmetatable",
"ipairs",
"load",
"loadfile",
"next",
"pairs",
"pcall",
"print",
"rawequal",
"rawget",
"rawlen",
"rawset",
"require",
"select",
"setmetatable",
"tonumber",
"tostring",
"type",
"warn",
"xpcall",
// standard library tables
"coroutine",
"debug",
"io",
"math",
"os",
"package",
"string",
"table",
"utf8",
// globals
"_G",
"_VERSION",
];
for name in &stdlib_globals {
self.globals.insert((*name).to_string());
}
self
}
}