Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/Scope.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
3
#include "Luau/Scope.h"
4
5
LUAU_FASTFLAG(LuauSolverV2);
6
7
namespace Luau
8
{
9
10
Scope::Scope(TypePackId returnType)
11
: parent(nullptr)
12
, returnType(returnType)
13
, level(TypeLevel())
14
{
15
}
16
17
Scope::Scope(const ScopePtr& parent, int subLevel)
18
: parent(parent)
19
, returnType(parent->returnType)
20
, level(parent->level.incr())
21
{
22
level = level.incr();
23
level.subLevel = subLevel;
24
}
25
26
void Scope::addBuiltinTypeBinding(const Name& name, const TypeFun& tyFun)
27
{
28
exportedTypeBindings[name] = tyFun;
29
builtinTypeNames.insert(name);
30
}
31
32
std::optional<TypeId> Scope::lookup(Symbol sym) const
33
{
34
auto r = const_cast<Scope*>(this)->lookupEx(sym);
35
if (r)
36
return r->first->typeId;
37
else
38
return std::nullopt;
39
}
40
41
std::optional<std::pair<TypeId, Scope*>> Scope::lookupEx(DefId def)
42
{
43
Scope* s = this;
44
45
while (true)
46
{
47
if (TypeId* it = s->lvalueTypes.find(def))
48
return std::pair{*it, s};
49
else if (TypeId* it = s->rvalueRefinements.find(def))
50
return std::pair{*it, s};
51
52
if (s->parent)
53
s = s->parent.get();
54
else
55
return std::nullopt;
56
}
57
}
58
59
std::optional<std::pair<Binding*, Scope*>> Scope::lookupEx(Symbol sym)
60
{
61
Scope* s = this;
62
63
while (true)
64
{
65
auto it = s->bindings.find(sym);
66
if (it != s->bindings.end())
67
return std::pair{&it->second, s};
68
69
if (s->parent)
70
s = s->parent.get();
71
else
72
return std::nullopt;
73
}
74
}
75
76
std::optional<TypeId> Scope::lookupUnrefinedType(DefId def) const
77
{
78
for (const Scope* current = this; current; current = current->parent.get())
79
{
80
if (auto ty = current->lvalueTypes.find(def))
81
return *ty;
82
}
83
84
return std::nullopt;
85
}
86
87
std::optional<TypeId> Scope::lookupRValueRefinementType(DefId def) const
88
{
89
for (const Scope* current = this; current; current = current->parent.get())
90
{
91
if (auto ty = current->rvalueRefinements.find(def))
92
return *ty;
93
}
94
95
return std::nullopt;
96
}
97
98
std::optional<TypeId> Scope::lookup(DefId def) const
99
{
100
for (const Scope* current = this; current; current = current->parent.get())
101
{
102
if (auto ty = current->rvalueRefinements.find(def))
103
return *ty;
104
if (auto ty = current->lvalueTypes.find(def))
105
return *ty;
106
}
107
108
return std::nullopt;
109
}
110
111
std::optional<TypeFun> Scope::lookupType(const Name& name) const
112
{
113
const Scope* scope = this;
114
while (true)
115
{
116
auto it = scope->exportedTypeBindings.find(name);
117
if (it != scope->exportedTypeBindings.end())
118
return it->second;
119
120
it = scope->privateTypeBindings.find(name);
121
if (it != scope->privateTypeBindings.end())
122
return it->second;
123
124
if (scope->parent)
125
scope = scope->parent.get();
126
else
127
return std::nullopt;
128
}
129
}
130
131
std::optional<TypeFun> Scope::lookupImportedType(const Name& moduleAlias, const Name& name) const
132
{
133
const Scope* scope = this;
134
while (scope)
135
{
136
auto it = scope->importedTypeBindings.find(moduleAlias);
137
if (it == scope->importedTypeBindings.end())
138
{
139
scope = scope->parent.get();
140
continue;
141
}
142
143
auto it2 = it->second.find(name);
144
if (it2 == it->second.end())
145
{
146
scope = scope->parent.get();
147
continue;
148
}
149
150
return it2->second;
151
}
152
153
return std::nullopt;
154
}
155
156
std::optional<TypePackId> Scope::lookupPack(const Name& name) const
157
{
158
const Scope* scope = this;
159
while (true)
160
{
161
auto it = scope->privateTypePackBindings.find(name);
162
if (it != scope->privateTypePackBindings.end())
163
return it->second;
164
165
if (scope->parent)
166
scope = scope->parent.get();
167
else
168
return std::nullopt;
169
}
170
}
171
172
std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bool traverseScopeChain) const
173
{
174
const Scope* scope = this;
175
176
while (scope)
177
{
178
for (const auto& [n, binding] : scope->bindings)
179
{
180
if (n.local && n.local->name == name.c_str())
181
return binding;
182
else if (n.global.value && n.global == name.c_str())
183
return binding;
184
}
185
186
scope = scope->parent.get();
187
188
if (!traverseScopeChain)
189
break;
190
}
191
192
return std::nullopt;
193
}
194
195
std::optional<std::pair<Symbol, Binding>> Scope::linearSearchForBindingPair(const std::string& name, bool traverseScopeChain) const
196
{
197
const Scope* scope = this;
198
199
while (scope)
200
{
201
for (auto& [n, binding] : scope->bindings)
202
{
203
if (n.local && n.local->name == name.c_str())
204
return {{n, binding}};
205
else if (n.global.value && n.global == name.c_str())
206
return {{n, binding}};
207
}
208
209
scope = scope->parent.get();
210
211
if (!traverseScopeChain)
212
break;
213
}
214
215
return std::nullopt;
216
}
217
218
// Updates the `this` scope with the assignments from the `childScope` including ones that doesn't exist in `this`.
219
void Scope::inheritAssignments(const ScopePtr& childScope)
220
{
221
for (const auto& [k, a] : childScope->lvalueTypes)
222
lvalueTypes[k] = a;
223
}
224
225
// Updates the `this` scope with the refinements from the `childScope` excluding ones that doesn't exist in `this`.
226
void Scope::inheritRefinements(const ScopePtr& childScope)
227
{
228
for (const auto& [k, a] : childScope->rvalueRefinements)
229
{
230
if (lookup(NotNull{k}))
231
rvalueRefinements[k] = a;
232
}
233
234
for (const auto& [k, a] : childScope->refinements)
235
{
236
Symbol symbol = getBaseSymbol(k);
237
if (lookup(symbol))
238
refinements[k] = a;
239
}
240
}
241
242
bool Scope::shouldWarnGlobal(std::string name) const
243
{
244
for (const Scope* current = this; current; current = current->parent.get())
245
{
246
if (current->globalsToWarn.contains(name))
247
return true;
248
}
249
return false;
250
}
251
252
std::optional<Location> Scope::isInvalidTypeAlias(const std::string& name) const
253
{
254
for (auto scope = this; scope; scope = scope->parent.get())
255
{
256
if (auto loc = scope->invalidTypeAliases.find(name))
257
return {*loc};
258
}
259
260
return std::nullopt;
261
}
262
263
NotNull<Scope> Scope::findNarrowestScopeContaining(Location location)
264
{
265
Scope* bestScope = this;
266
267
bool didNarrow;
268
do
269
{
270
didNarrow = false;
271
for (auto scope : bestScope->children)
272
{
273
if (scope->location.encloses(location))
274
{
275
bestScope = scope.get();
276
didNarrow = true;
277
break;
278
}
279
}
280
} while (didNarrow && bestScope->children.size() > 0);
281
282
return NotNull{bestScope};
283
}
284
285
286
bool subsumesStrict(Scope* left, Scope* right)
287
{
288
if (!left || !right)
289
return false;
290
291
while (right)
292
{
293
if (right->parent.get() == left)
294
return true;
295
296
right = right->parent.get();
297
}
298
299
return false;
300
}
301
302
bool subsumes(Scope* left, Scope* right)
303
{
304
if (!left || !right)
305
return false;
306
307
return left == right || subsumesStrict(left, right);
308
}
309
310
} // namespace Luau
311
312