gdscript_api/gdscript_layer.rs
1//! The hand-authored GDScript layer the engine dump omits (Playbook §4.4).
2//!
3//! `extension_api.json` describes the engine (classes, builtins, `@GlobalScope` utilities) but
4//! not the *language* surface GDScript adds on top: the `@GlobalScope`/`@GDScript`
5//! pseudo-constants (`PI`/`TAU`/`INF`/`NAN`) and the GDScript builtin functions
6//! (`preload`/`load`/`range`/`len`/…), whose return types are decided here rather than read
7//! from any dump. `gdscript-hir` consults these during global name resolution.
8//!
9//! Types are tagged with the coarse, model-independent [`LayerTy`] (resolved to a `gdscript-hir`
10//! `Ty` by the consumer) because real [`crate::BuiltinId`]s only exist after the model loads.
11
12/// A coarse type tag for hand-authored symbols, mapped to a `gdscript-hir` `Ty` by the consumer.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum LayerTy {
15 /// `float`.
16 Float,
17 /// `int`.
18 Int,
19 /// `bool`.
20 Bool,
21 /// `String`.
22 Str,
23 /// Bare `Array` (`Array[Variant]`).
24 Array,
25 /// Bare `Dictionary`.
26 Dictionary,
27 /// `Color`.
28 Color,
29 /// The dynamic `Variant` top type.
30 Variant,
31 /// The Phase-3 seam marker — distinct from `Variant`, never warns (e.g. `preload`).
32 Unknown,
33 /// `void`.
34 Void,
35}
36
37/// A `@GlobalScope`/`@GDScript` pseudo-constant (`PI`, `TAU`, `INF`, `NAN`).
38#[derive(Debug, Clone)]
39pub struct GlobalConst {
40 /// The constant name.
41 pub name: &'static str,
42 /// Its type.
43 pub ty: LayerTy,
44}
45
46/// A GDScript builtin function (`preload`, `range`, `len`, …) — distinct from the
47/// `@GlobalScope` *utility* functions, which come from the JSON.
48#[derive(Debug, Clone)]
49pub struct BuiltinFn {
50 /// The function name.
51 pub name: &'static str,
52 /// Minimum argument count.
53 pub min_args: u8,
54 /// Maximum argument count, or `None` for variadic.
55 pub max_args: Option<u8>,
56 /// The decided return type. `preload`/`load` are refined by `gdscript-hir` per the
57 /// literal-vs-variable argument rule (Playbook §4.4); this is the conservative default.
58 pub ret: LayerTy,
59}
60
61/// The pseudo-constants `extension_api.json` reports as empty `global_constants` in 4.5.
62#[must_use]
63pub fn global_consts() -> Vec<GlobalConst> {
64 use LayerTy::Float;
65 vec![
66 GlobalConst {
67 name: "PI",
68 ty: Float,
69 },
70 GlobalConst {
71 name: "TAU",
72 ty: Float,
73 },
74 GlobalConst {
75 name: "INF",
76 ty: Float,
77 },
78 GlobalConst {
79 name: "NAN",
80 ty: Float,
81 },
82 ]
83}
84
85/// The GDScript builtin functions (the `@GDScript` surface). The list grows as features need
86/// it; these are the ones inference and completion rely on in Phase 2.
87#[must_use]
88pub fn builtin_fns() -> Vec<BuiltinFn> {
89 use LayerTy::{Array, Bool, Color, Dictionary, Int, Str, Unknown, Variant, Void};
90 vec![
91 // Two `@GlobalScope` utility functions the extracted blob omits (the extraction filtered
92 // them; they are real bare globals — the corpus calls both). Hand-authored here, exactly
93 // what this layer is for, so bare calls resolve and never false-flag as UNDEFINED.
94 BuiltinFn {
95 name: "Color8",
96 min_args: 3,
97 max_args: Some(4),
98 ret: Color,
99 },
100 BuiltinFn {
101 name: "is_instance_of",
102 min_args: 2,
103 max_args: Some(2),
104 ret: Bool,
105 },
106 // The REST of the `@GDScript` pseudo-class surface (vendor .../doc/classes/@GDScript.xml
107 // lists 15 methods; these complete the set). They exist in no extracted table — with the
108 // A1 `UNDEFINED_FUNCTION` armed, an uncovered one false-flags common code (`print_debug`).
109 BuiltinFn {
110 name: "convert",
111 min_args: 2,
112 max_args: Some(2),
113 ret: Variant,
114 },
115 BuiltinFn {
116 name: "dict_to_inst",
117 min_args: 1,
118 max_args: Some(1),
119 ret: Variant,
120 },
121 BuiltinFn {
122 name: "get_stack",
123 min_args: 0,
124 max_args: Some(0),
125 ret: Array,
126 },
127 BuiltinFn {
128 name: "inst_to_dict",
129 min_args: 1,
130 max_args: Some(1),
131 ret: Dictionary,
132 },
133 BuiltinFn {
134 name: "ord",
135 min_args: 1,
136 max_args: Some(1),
137 ret: Int,
138 },
139 BuiltinFn {
140 name: "print_debug",
141 min_args: 0,
142 max_args: None,
143 ret: Void,
144 },
145 BuiltinFn {
146 name: "print_stack",
147 min_args: 0,
148 max_args: Some(0),
149 ret: Void,
150 },
151 BuiltinFn {
152 name: "type_exists",
153 min_args: 1,
154 max_args: Some(1),
155 ret: Bool,
156 },
157 // `preload(path)` resolves to a script/resource — opaque in Phase 2 (the seam).
158 BuiltinFn {
159 name: "preload",
160 min_args: 1,
161 max_args: Some(1),
162 ret: Unknown,
163 },
164 // `load(path)` returns a `Resource` at runtime, but the concrete script/resource type is
165 // unknowable statically (the arg may be a variable, and even a literal is a *runtime*
166 // call — NOT a compile-time constant like `preload`). Model it as the seam (`Unknown`) so
167 // `var r := load(...)` neither warns (`INFERENCE_ON_VARIANT`) nor cascades, and `load` is
168 // never aliased to `preload` (Playbook §3.M3 / D5 — both literal and variable args opaque).
169 BuiltinFn {
170 name: "load",
171 min_args: 1,
172 max_args: Some(1),
173 ret: Unknown,
174 },
175 BuiltinFn {
176 name: "range",
177 min_args: 1,
178 max_args: Some(3),
179 ret: Array,
180 },
181 BuiltinFn {
182 name: "len",
183 min_args: 1,
184 max_args: Some(1),
185 ret: Int,
186 },
187 BuiltinFn {
188 name: "char",
189 min_args: 1,
190 max_args: Some(1),
191 ret: Str,
192 },
193 BuiltinFn {
194 name: "assert",
195 min_args: 1,
196 max_args: Some(2),
197 ret: Void,
198 },
199 ]
200}