Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Analysis/src/AutocompleteCore.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 "AutocompleteCore.h"
4
5
#include "Luau/Ast.h"
6
#include "Luau/AstQuery.h"
7
#include "Luau/AutocompleteTypes.h"
8
9
#include "Luau/BuiltinDefinitions.h"
10
#include "Luau/Common.h"
11
#include "Luau/FileResolver.h"
12
#include "Luau/Frontend.h"
13
#include "Luau/TimeTrace.h"
14
#include "Luau/ToString.h"
15
#include "Luau/Subtyping.h"
16
#include "Luau/TypeInfer.h"
17
#include "Luau/TypePack.h"
18
19
#include <algorithm>
20
#include <array>
21
#include <string>
22
#include <string_view>
23
#include <unordered_set>
24
#include <utility>
25
26
LUAU_FASTINT(LuauTypeInferIterationLimit)
27
LUAU_FASTINT(LuauTypeInferRecursionLimit)
28
LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames)
29
LUAU_FASTFLAGVARIABLE(LuauAutocompleteFunctionCallArgTails2)
30
LUAU_FASTFLAGVARIABLE(LuauACOnMTTWriteOnlyPropNoCrash)
31
32
static constexpr std::array<std::string_view, 12> kStatementStartingKeywords =
33
{"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
34
35
static constexpr std::array<std::string_view, 6> kHotComments = {"nolint", "nocheck", "nonstrict", "strict", "optimize", "native"};
36
37
static const std::string kKnownAttributes[] = {"checked", "deprecated", "native"};
38
39
namespace Luau
40
{
41
42
static bool alreadyHasParens(const std::vector<AstNode*>& nodes)
43
{
44
auto iter = nodes.rbegin();
45
while (iter != nodes.rend() &&
46
((*iter)->is<AstExprLocal>() || (*iter)->is<AstExprGlobal>() || (*iter)->is<AstExprIndexName>() || (*iter)->is<AstExprIndexExpr>()))
47
{
48
iter++;
49
}
50
51
if (iter == nodes.rend() || iter == nodes.rbegin())
52
{
53
return false;
54
}
55
56
if (AstExprCall* call = (*iter)->as<AstExprCall>())
57
{
58
return call->func == *(iter - 1);
59
}
60
61
return false;
62
}
63
64
static ParenthesesRecommendation getParenRecommendationForFunc(const FunctionType* func, const std::vector<AstNode*>& nodes)
65
{
66
if (alreadyHasParens(nodes))
67
{
68
return ParenthesesRecommendation::None;
69
}
70
71
auto idxExpr = nodes.back()->as<AstExprIndexName>();
72
bool hasImplicitSelf = idxExpr && idxExpr->op == ':';
73
auto [argTypes, argVariadicPack] = Luau::flatten(func->argTypes);
74
75
if (argVariadicPack.has_value() && isVariadic(*argVariadicPack))
76
return ParenthesesRecommendation::CursorInside;
77
78
bool noArgFunction = argTypes.empty() || (hasImplicitSelf && argTypes.size() == 1);
79
return noArgFunction ? ParenthesesRecommendation::CursorAfter : ParenthesesRecommendation::CursorInside;
80
}
81
82
static ParenthesesRecommendation getParenRecommendationForIntersect(const IntersectionType* intersect, const std::vector<AstNode*>& nodes)
83
{
84
ParenthesesRecommendation rec = ParenthesesRecommendation::None;
85
for (Luau::TypeId partId : intersect->parts)
86
{
87
partId = follow(partId);
88
if (auto partFunc = Luau::get<FunctionType>(partId))
89
{
90
rec = std::max(rec, getParenRecommendationForFunc(partFunc, nodes));
91
}
92
else
93
{
94
return ParenthesesRecommendation::None;
95
}
96
}
97
return rec;
98
}
99
100
static ParenthesesRecommendation getParenRecommendation(TypeId id, const std::vector<AstNode*>& nodes, TypeCorrectKind typeCorrect)
101
{
102
// If element is already type-correct, even a function should be inserted without parenthesis
103
if (typeCorrect == TypeCorrectKind::Correct)
104
return ParenthesesRecommendation::None;
105
106
id = Luau::follow(id);
107
if (auto func = get<FunctionType>(id))
108
{
109
return getParenRecommendationForFunc(func, nodes);
110
}
111
else if (auto intersect = get<IntersectionType>(id))
112
{
113
return getParenRecommendationForIntersect(intersect, nodes);
114
}
115
return ParenthesesRecommendation::None;
116
}
117
118
static std::optional<TypeId> findExpectedTypeAt(const Module& module, AstNode* node, Position position)
119
{
120
auto expr = node->asExpr();
121
if (!expr)
122
return std::nullopt;
123
124
// Extra care for first function call argument location
125
// When we don't have anything inside () yet, we also don't have an AST node to base our lookup
126
if (AstExprCall* exprCall = expr->as<AstExprCall>())
127
{
128
if ((exprCall->args.size == 0 && exprCall->argLocation.contains(position)) ||
129
(FFlag::LuauAutocompleteFunctionCallArgTails2 && exprCall->args.size > 0 && (*exprCall->args.begin())->as<AstExprError>()))
130
{
131
auto it = module.astTypes.find(exprCall->func);
132
133
if (!it)
134
return std::nullopt;
135
136
const FunctionType* ftv = get<FunctionType>(follow(*it));
137
138
if (!ftv)
139
return std::nullopt;
140
141
auto [head, tail] = flatten(ftv->argTypes);
142
unsigned index = exprCall->self ? 1 : 0;
143
144
if (index < head.size())
145
return head[index];
146
else if (FFlag::LuauAutocompleteFunctionCallArgTails2 && index == head.size() && tail.has_value() && isVariadic(*tail))
147
return first(*tail);
148
149
return std::nullopt;
150
}
151
}
152
153
auto it = module.astExpectedTypes.find(expr);
154
if (!it)
155
return std::nullopt;
156
157
return *it;
158
}
159
160
static bool checkTypeMatch(
161
const Module& module,
162
TypeId subTy,
163
TypeId superTy,
164
NotNull<Scope> scope,
165
TypeArena* typeArena,
166
NotNull<BuiltinTypes> builtinTypes
167
)
168
{
169
InternalErrorReporter iceReporter;
170
UnifierSharedState unifierState(&iceReporter);
171
Normalizer normalizer{typeArena, builtinTypes, NotNull{&unifierState}, module.checkedInNewSolver ? SolverMode::New : SolverMode::Old};
172
if (module.checkedInNewSolver)
173
{
174
TypeCheckLimits limits;
175
TypeFunctionRuntime typeFunctionRuntime{
176
NotNull{&iceReporter}, NotNull{&limits}
177
}; // TODO: maybe subtyping checks should not invoke user-defined type function runtime
178
179
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
180
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
181
182
Subtyping subtyping{builtinTypes, NotNull{typeArena}, NotNull{&normalizer}, NotNull{&typeFunctionRuntime}, NotNull{&iceReporter}};
183
184
return subtyping.isSubtype(subTy, superTy, scope).isSubtype;
185
}
186
else
187
{
188
Unifier unifier(NotNull<Normalizer>{&normalizer}, scope, Location(), Variance::Covariant);
189
190
// Cost of normalization can be too high for autocomplete response time requirements
191
unifier.normalize = false;
192
unifier.checkInhabited = false;
193
194
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
195
unifierState.counters.iterationLimit = FInt::LuauTypeInferIterationLimit;
196
197
return unifier.canUnify(subTy, superTy).empty();
198
}
199
}
200
201
static TypeCorrectKind checkTypeCorrectKind(
202
const Module& module,
203
TypeArena* typeArena,
204
NotNull<BuiltinTypes> builtinTypes,
205
AstNode* node,
206
Position position,
207
TypeId ty
208
)
209
{
210
ty = follow(ty);
211
212
LUAU_ASSERT(module.hasModuleScope());
213
214
NotNull<Scope> moduleScope{module.getModuleScope().get()};
215
216
auto typeAtPosition = findExpectedTypeAt(module, node, position);
217
218
if (!typeAtPosition)
219
return TypeCorrectKind::None;
220
221
TypeId expectedType = follow(*typeAtPosition);
222
223
auto checkFunctionType = [typeArena, builtinTypes, moduleScope, &expectedType, &module](const FunctionType* ftv)
224
{
225
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
226
return checkTypeMatch(module, *firstRetTy, expectedType, moduleScope, typeArena, builtinTypes);
227
228
return false;
229
};
230
231
// We also want to suggest functions that return compatible result
232
if (const FunctionType* ftv = get<FunctionType>(ty); ftv && checkFunctionType(ftv))
233
{
234
return TypeCorrectKind::CorrectFunctionResult;
235
}
236
else if (const IntersectionType* itv = get<IntersectionType>(ty))
237
{
238
for (TypeId id : itv->parts)
239
{
240
id = follow(id);
241
242
if (const FunctionType* ftv = get<FunctionType>(id); ftv && checkFunctionType(ftv))
243
{
244
return TypeCorrectKind::CorrectFunctionResult;
245
}
246
}
247
}
248
249
return checkTypeMatch(module, ty, expectedType, moduleScope, typeArena, builtinTypes) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
250
}
251
252
enum class PropIndexType
253
{
254
Point,
255
Colon,
256
Key,
257
};
258
259
static void autocompleteProps(
260
const Module& module,
261
TypeArena* typeArena,
262
NotNull<BuiltinTypes> builtinTypes,
263
TypeId rootTy,
264
TypeId ty,
265
PropIndexType indexType,
266
const std::vector<AstNode*>& nodes,
267
AutocompleteEntryMap& result,
268
std::unordered_set<TypeId>& seen,
269
std::optional<const ExternType*> containingExternType = std::nullopt
270
)
271
{
272
rootTy = follow(rootTy);
273
ty = follow(ty);
274
275
if (seen.count(ty))
276
return;
277
seen.insert(ty);
278
279
auto isWrongIndexer = [typeArena, builtinTypes, &module, rootTy, indexType](Luau::TypeId type)
280
{
281
if (indexType == PropIndexType::Key)
282
return false;
283
284
bool calledWithSelf = indexType == PropIndexType::Colon;
285
286
auto isCompatibleCall = [typeArena, builtinTypes, &module, rootTy, calledWithSelf](const FunctionType* ftv)
287
{
288
// Strong match with definition is a success
289
if (calledWithSelf == ftv->hasSelf)
290
return true;
291
292
// Calls on extern types require strict match between how function is declared and how it's called
293
if (get<ExternType>(rootTy))
294
return false;
295
296
// When called with ':', but declared without 'self', it is invalid if a function has incompatible first argument or no arguments at all
297
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
298
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
299
{
300
if (checkTypeMatch(module, rootTy, *firstArgTy, NotNull{module.getModuleScope().get()}, typeArena, builtinTypes))
301
return calledWithSelf;
302
}
303
304
return !calledWithSelf;
305
};
306
307
if (const FunctionType* ftv = get<FunctionType>(type))
308
return !isCompatibleCall(ftv);
309
310
// For intersections, any part that is successful makes the whole call successful
311
if (const IntersectionType* itv = get<IntersectionType>(type))
312
{
313
for (auto subType : itv->parts)
314
{
315
if (const FunctionType* ftv = get<FunctionType>(Luau::follow(subType)))
316
{
317
if (isCompatibleCall(ftv))
318
return false;
319
}
320
}
321
}
322
323
return calledWithSelf;
324
};
325
326
auto maybeFillSingletonProp = [&](TypeId type)
327
{
328
if (auto singletonTy = get<SingletonType>(type))
329
{
330
if (auto stringSingleton = get<StringSingleton>(singletonTy))
331
{
332
333
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key
334
? TypeCorrectKind::Correct
335
: checkTypeCorrectKind(module, typeArena, builtinTypes, nodes.back(), {{}, {}}, type);
336
337
ParenthesesRecommendation parens =
338
indexType == PropIndexType::Key ? ParenthesesRecommendation::None : getParenRecommendation(ty, nodes, typeCorrect);
339
340
result[stringSingleton->value] = AutocompleteEntry{
341
AutocompleteEntryKind::String,
342
type,
343
/* deprecated */ false,
344
isWrongIndexer(type),
345
typeCorrect,
346
containingExternType,
347
std::nullopt,
348
std::nullopt,
349
{},
350
parens,
351
{},
352
indexType == PropIndexType::Colon
353
};
354
}
355
}
356
};
357
358
auto fillProps = [&](const ExternType::Props& props)
359
{
360
for (const auto& [name, prop] : props)
361
{
362
// We are walking up the class hierarchy, so if we encounter a property that we have
363
// already populated, it takes precedence over the property we found just now.
364
if (result.count(name) == 0 && name != kParseNameError)
365
{
366
Luau::TypeId type;
367
if (auto ty = prop.readTy)
368
type = follow(*ty);
369
else
370
continue;
371
372
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key
373
? TypeCorrectKind::Correct
374
: checkTypeCorrectKind(module, typeArena, builtinTypes, nodes.back(), {{}, {}}, type);
375
376
ParenthesesRecommendation parens =
377
indexType == PropIndexType::Key ? ParenthesesRecommendation::None : getParenRecommendation(type, nodes, typeCorrect);
378
379
result[name] = AutocompleteEntry{
380
AutocompleteEntryKind::Property,
381
type,
382
prop.deprecated,
383
isWrongIndexer(type),
384
typeCorrect,
385
containingExternType,
386
&prop,
387
prop.documentationSymbol,
388
{},
389
parens,
390
{},
391
indexType == PropIndexType::Colon
392
};
393
}
394
}
395
};
396
397
auto fillMetatableProps = [&](const TableType* mtable)
398
{
399
auto indexIt = mtable->props.find("__index");
400
if (indexIt != mtable->props.end())
401
{
402
#ifndef __EMSCRIPTEN__
403
// EMSDK cannot compile the flag-off branch, so force the flag on with defines here.
404
// Delete these conditionals when this flag is removed.
405
if (FFlag::LuauACOnMTTWriteOnlyPropNoCrash)
406
#endif
407
{
408
TypeId followed = indexIt->second.readTy.value_or(nullptr);
409
if (followed == nullptr)
410
return;
411
followed = follow(followed);
412
LUAU_ASSERT(followed);
413
414
if (get<TableType>(followed) || get<MetatableType>(followed))
415
{
416
autocompleteProps(module, typeArena, builtinTypes, rootTy, followed, indexType, nodes, result, seen);
417
}
418
else if (auto indexFunction = get<FunctionType>(followed))
419
{
420
std::optional<TypeId> indexFunctionResult = first(indexFunction->retTypes);
421
if (indexFunctionResult)
422
autocompleteProps(module, typeArena, builtinTypes, rootTy, *indexFunctionResult, indexType, nodes, result, seen);
423
}
424
}
425
#ifndef __EMSCRIPTEN__
426
else
427
{
428
TypeId followed;
429
if (auto propTy = indexIt->second.readTy)
430
{
431
followed = follow(*propTy);
432
}
433
if (get<TableType>(followed) || get<MetatableType>(followed))
434
{
435
autocompleteProps(module, typeArena, builtinTypes, rootTy, followed, indexType, nodes, result, seen);
436
}
437
else if (auto indexFunction = get<FunctionType>(followed))
438
{
439
std::optional<TypeId> indexFunctionResult = first(indexFunction->retTypes);
440
if (indexFunctionResult)
441
autocompleteProps(module, typeArena, builtinTypes, rootTy, *indexFunctionResult, indexType, nodes, result, seen);
442
}
443
}
444
#endif
445
}
446
};
447
448
if (auto cls = get<ExternType>(ty))
449
{
450
containingExternType = containingExternType.value_or(cls);
451
fillProps(cls->props);
452
if (cls->parent)
453
autocompleteProps(module, typeArena, builtinTypes, rootTy, *cls->parent, indexType, nodes, result, seen, containingExternType);
454
}
455
else if (auto tbl = get<TableType>(ty))
456
{
457
fillProps(tbl->props);
458
if (tbl->indexer && indexType == PropIndexType::Point)
459
{
460
auto indexerTy = follow(tbl->indexer->indexType);
461
if (auto utv = get<UnionType>(indexerTy))
462
{
463
for (auto option : utv)
464
maybeFillSingletonProp(option);
465
}
466
else
467
maybeFillSingletonProp(indexerTy);
468
}
469
}
470
else if (auto mt = get<MetatableType>(ty))
471
{
472
autocompleteProps(module, typeArena, builtinTypes, rootTy, mt->table, indexType, nodes, result, seen);
473
474
if (auto mtable = get<TableType>(follow(mt->metatable)))
475
fillMetatableProps(mtable);
476
}
477
else if (auto i = get<IntersectionType>(ty))
478
{
479
// Complete all properties in every variant
480
for (TypeId ty : i->parts)
481
{
482
AutocompleteEntryMap inner;
483
std::unordered_set<TypeId> innerSeen = seen;
484
485
autocompleteProps(module, typeArena, builtinTypes, rootTy, ty, indexType, nodes, inner, innerSeen);
486
487
for (auto& pair : inner)
488
result.insert(pair);
489
}
490
}
491
else if (auto u = get<UnionType>(ty))
492
{
493
// Complete all properties common to all variants
494
auto iter = begin(u);
495
auto endIter = end(u);
496
497
while (iter != endIter)
498
{
499
if (isNil(*iter))
500
++iter;
501
else
502
break;
503
}
504
505
if (iter == endIter)
506
return;
507
508
autocompleteProps(module, typeArena, builtinTypes, rootTy, *iter, indexType, nodes, result, seen);
509
510
++iter;
511
512
while (iter != endIter)
513
{
514
AutocompleteEntryMap inner;
515
std::unordered_set<TypeId> innerSeen;
516
517
// If we don't do this, and we have the misfortune of receiving a
518
// recursive union like:
519
//
520
// t1 where t1 = t1 | ExternType
521
//
522
// Then we are on a one way journey to a stack overflow.
523
for (auto ty : seen)
524
{
525
if (is<UnionType, IntersectionType>(ty))
526
innerSeen.insert(ty);
527
}
528
529
if (isNil(*iter))
530
{
531
++iter;
532
continue;
533
}
534
535
autocompleteProps(module, typeArena, builtinTypes, rootTy, *iter, indexType, nodes, inner, innerSeen);
536
537
std::unordered_set<std::string> toRemove;
538
539
for (const auto& [k, v] : result)
540
{
541
(void)v;
542
if (!inner.count(k))
543
toRemove.insert(k);
544
}
545
546
for (const std::string& k : toRemove)
547
result.erase(k);
548
549
++iter;
550
}
551
}
552
else if (auto pt = get<PrimitiveType>(ty))
553
{
554
if (pt->metatable)
555
{
556
if (auto mtable = get<TableType>(*pt->metatable))
557
fillMetatableProps(mtable);
558
}
559
}
560
else if (get<StringSingleton>(get<SingletonType>(ty)))
561
{
562
autocompleteProps(module, typeArena, builtinTypes, rootTy, builtinTypes->stringType, indexType, nodes, result, seen);
563
}
564
}
565
566
static void autocompleteKeywords(const std::vector<AstNode*>& ancestry, Position position, AutocompleteEntryMap& result)
567
{
568
LUAU_ASSERT(!ancestry.empty());
569
570
AstNode* node = ancestry.back();
571
572
if (!node->is<AstExprFunction>() && node->asExpr())
573
{
574
// This is not strictly correct. We should recommend `and` and `or` only after
575
// another expression, not at the start of a new one. We should only recommend
576
// `not` at the start of an expression. Detecting either case reliably is quite
577
// complex, however; this is good enough for now.
578
579
// These are not context-sensitive keywords, so we can unconditionally assign.
580
result["and"] = {AutocompleteEntryKind::Keyword};
581
result["or"] = {AutocompleteEntryKind::Keyword};
582
result["not"] = {AutocompleteEntryKind::Keyword};
583
}
584
}
585
586
static void autocompleteProps(
587
const Module& module,
588
TypeArena* typeArena,
589
NotNull<BuiltinTypes> builtinTypes,
590
TypeId ty,
591
PropIndexType indexType,
592
const std::vector<AstNode*>& nodes,
593
AutocompleteEntryMap& result
594
)
595
{
596
std::unordered_set<TypeId> seen;
597
autocompleteProps(module, typeArena, builtinTypes, ty, ty, indexType, nodes, result, seen);
598
}
599
600
AutocompleteEntryMap autocompleteProps(
601
const Module& module,
602
TypeArena* typeArena,
603
NotNull<BuiltinTypes> builtinTypes,
604
TypeId ty,
605
PropIndexType indexType,
606
const std::vector<AstNode*>& nodes
607
)
608
{
609
AutocompleteEntryMap result;
610
autocompleteProps(module, typeArena, builtinTypes, ty, indexType, nodes, result);
611
return result;
612
}
613
614
AutocompleteEntryMap autocompleteModuleTypes(const Module& module, const ScopePtr& scopeAtPosition, Position position, std::string_view moduleName)
615
{
616
AutocompleteEntryMap result;
617
ScopePtr startScope = scopeAtPosition;
618
for (ScopePtr& scope = startScope; scope; scope = scope->parent)
619
{
620
if (auto it = scope->importedTypeBindings.find(std::string(moduleName)); it != scope->importedTypeBindings.end())
621
{
622
for (const auto& [name, ty] : it->second)
623
result[name] = AutocompleteEntry{AutocompleteEntryKind::Type, ty.type};
624
625
break;
626
}
627
}
628
629
return result;
630
}
631
632
static void autocompleteStringSingleton(TypeId ty, bool addQuotes, AstNode* node, Position position, AutocompleteEntryMap& result)
633
{
634
if (position == node->location.begin || position == node->location.end)
635
{
636
if (auto str = node->as<AstExprConstantString>(); str && str->isQuoted())
637
return;
638
else if (node->is<AstExprInterpString>())
639
return;
640
}
641
642
auto formatKey = [addQuotes](const std::string& key)
643
{
644
if (addQuotes)
645
return "\"" + escape(key) + "\"";
646
647
return escape(key);
648
};
649
650
ty = follow(ty);
651
652
if (auto ss = get<StringSingleton>(get<SingletonType>(ty)))
653
{
654
// This is purposefully `try_emplace` as we don't want to override any existing entries.
655
result.try_emplace(formatKey(ss->value), AutocompleteEntry{AutocompleteEntryKind::String, ty, false, false, TypeCorrectKind::Correct});
656
}
657
else if (auto uty = get<UnionType>(ty))
658
{
659
for (auto el : uty)
660
{
661
if (auto ss = get<StringSingleton>(get<SingletonType>(el)))
662
{
663
// This is purposefully `try_emplace` as we don't want to override any existing entries.
664
result.try_emplace(
665
formatKey(ss->value), AutocompleteEntry{AutocompleteEntryKind::String, ty, false, false, TypeCorrectKind::Correct}
666
);
667
}
668
}
669
}
670
};
671
672
static bool canSuggestInferredType(TypeId ty)
673
{
674
ty = follow(ty);
675
676
// No point in suggesting 'any', invalid to suggest others
677
if (get<AnyType>(ty) || get<ErrorType>(ty) || get<GenericType>(ty) || get<FreeType>(ty))
678
return false;
679
680
// No syntax for unnamed tables with a metatable
681
if (get<MetatableType>(ty))
682
return false;
683
684
if (const TableType* ttv = get<TableType>(ty))
685
{
686
if (ttv->name)
687
return true;
688
689
if (ttv->syntheticName)
690
return false;
691
}
692
693
// We might still have a type with cycles or one that is too long, we'll check that later
694
return true;
695
}
696
697
static bool canSuggestInferredType(TypePackId ty)
698
{
699
ty = follow(ty);
700
701
if (get<ErrorTypePack>(ty) || get<GenericTypePack>(ty) || get<FreeTypePack>(ty))
702
return false;
703
704
auto [head, tail] = flatten(ty);
705
for (TypeId headTy : head)
706
{
707
if (!canSuggestInferredType(headTy))
708
return false;
709
}
710
711
// We skip recursively checking the tail here, as it could be cyclic and
712
// cause stack overflows. This case is handled later.
713
return true;
714
}
715
716
// Walk complex type trees to find the element that is being edited
717
static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Position position);
718
719
static std::optional<TypeId> findTypeElementAt(const AstTypeList& astTypeList, TypePackId tp, Position position)
720
{
721
for (size_t i = 0; i < astTypeList.types.size; i++)
722
{
723
AstType* type = astTypeList.types.data[i];
724
725
if (type->location.containsClosed(position))
726
{
727
auto [head, _] = flatten(tp);
728
729
if (i < head.size())
730
return findTypeElementAt(type, head[i], position);
731
}
732
}
733
734
if (AstTypePack* argTp = astTypeList.tailType)
735
{
736
if (auto variadic = argTp->as<AstTypePackVariadic>())
737
{
738
if (variadic->location.containsClosed(position))
739
{
740
auto [_, tail] = flatten(tp);
741
742
if (tail)
743
{
744
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*tail)))
745
return findTypeElementAt(variadic->variadicType, vtp->ty, position);
746
}
747
}
748
}
749
}
750
751
return {};
752
}
753
754
static std::optional<TypeId> findTypeElementAt(AstTypePack* astTypePack, TypePackId tp, Position position)
755
{
756
if (const auto typePack = astTypePack->as<AstTypePackExplicit>())
757
{
758
return findTypeElementAt(typePack->typeList, tp, position);
759
}
760
else if (const auto variadic = astTypePack->as<AstTypePackVariadic>())
761
{
762
if (variadic->location.containsClosed(position))
763
{
764
auto [_, tail] = flatten(tp);
765
766
if (tail)
767
{
768
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*tail)))
769
return findTypeElementAt(variadic->variadicType, vtp->ty, position);
770
}
771
}
772
}
773
774
return {};
775
}
776
777
static std::optional<TypeId> findTypeElementAt(AstType* astType, TypeId ty, Position position)
778
{
779
ty = follow(ty);
780
781
if (astType->is<AstTypeReference>())
782
return ty;
783
784
if (astType->is<AstTypeError>())
785
return ty;
786
787
if (AstTypeFunction* type = astType->as<AstTypeFunction>())
788
{
789
const FunctionType* ftv = get<FunctionType>(ty);
790
791
if (!ftv)
792
return {};
793
794
if (auto element = findTypeElementAt(type->argTypes, ftv->argTypes, position))
795
return element;
796
797
if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position))
798
return element;
799
}
800
801
// It's possible to walk through other types like intrsection and unions if we find value in doing that
802
return {};
803
}
804
805
std::optional<TypeId> getLocalTypeInScopeAt(const Module& module, const ScopePtr& scopeAtPosition, Position position, AstLocal* local)
806
{
807
if (ScopePtr scope = scopeAtPosition)
808
{
809
for (const auto& [name, binding] : scope->bindings)
810
{
811
if (name == local)
812
return binding.typeId;
813
}
814
}
815
816
return {};
817
}
818
819
template<typename T>
820
static std::optional<std::string> tryToStringDetailed(const ScopePtr& scope, T ty, bool functionTypeArguments)
821
{
822
ToStringOptions opts;
823
opts.useLineBreaks = false;
824
opts.hideTableKind = true;
825
opts.functionTypeArguments = functionTypeArguments;
826
opts.scope = scope;
827
ToStringResult name = toStringDetailed(ty, opts);
828
829
if (name.error || name.invalid || name.cycle || name.truncated)
830
return std::nullopt;
831
832
return name.name;
833
}
834
835
static std::optional<Name> tryGetTypeNameInScope(ScopePtr scope, TypeId ty, bool functionTypeArguments = false)
836
{
837
if (!canSuggestInferredType(ty))
838
return std::nullopt;
839
840
return tryToStringDetailed(scope, ty, functionTypeArguments);
841
}
842
843
static std::optional<Name> tryGetTypeNameInScope(ScopePtr scope, TypePackId tp, bool functionTypeArguments = false)
844
{
845
if (!canSuggestInferredType(tp))
846
return std::nullopt;
847
848
return tryToStringDetailed(scope, tp, functionTypeArguments);
849
}
850
851
static bool tryAddTypeCorrectSuggestion(AutocompleteEntryMap& result, ScopePtr scope, AstType* topType, TypeId inferredType, Position position)
852
{
853
std::optional<TypeId> ty;
854
855
if (topType)
856
ty = findTypeElementAt(topType, inferredType, position);
857
else
858
ty = inferredType;
859
860
if (!ty)
861
return false;
862
863
if (auto name = tryGetTypeNameInScope(std::move(scope), *ty))
864
{
865
if (auto it = result.find(*name); it != result.end())
866
it->second.typeCorrect = TypeCorrectKind::Correct;
867
else
868
result[*name] = AutocompleteEntry{AutocompleteEntryKind::Type, *ty, false, false, TypeCorrectKind::Correct};
869
870
return true;
871
}
872
873
return false;
874
}
875
876
static std::optional<TypeId> tryGetTypePackTypeAt(TypePackId tp, size_t index)
877
{
878
auto [tpHead, tpTail] = flatten(tp);
879
880
if (index < tpHead.size())
881
return tpHead[index];
882
883
// Infinite tail
884
if (tpTail)
885
{
886
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*tpTail)))
887
return vtp->ty;
888
}
889
890
return {};
891
}
892
893
template<typename T>
894
std::optional<const T*> returnFirstNonnullOptionOfType(const UnionType* utv)
895
{
896
std::optional<const T*> ret;
897
for (TypeId subTy : utv)
898
{
899
if (isNil(subTy))
900
continue;
901
902
if (const T* ftv = get<T>(follow(subTy)))
903
{
904
if (ret.has_value())
905
{
906
return std::nullopt;
907
}
908
ret = ftv;
909
}
910
else
911
{
912
return std::nullopt;
913
}
914
}
915
return ret;
916
}
917
918
static std::optional<bool> functionIsExpectedAt(const Module& module, AstNode* node, Position position)
919
{
920
auto typeAtPosition = findExpectedTypeAt(module, node, position);
921
922
if (!typeAtPosition)
923
return std::nullopt;
924
925
TypeId expectedType = follow(*typeAtPosition);
926
927
if (get<FunctionType>(expectedType))
928
return true;
929
930
if (const IntersectionType* itv = get<IntersectionType>(expectedType))
931
{
932
return std::all_of(
933
begin(itv->parts),
934
end(itv->parts),
935
[](auto&& ty)
936
{
937
return get<FunctionType>(Luau::follow(ty)) != nullptr;
938
}
939
);
940
}
941
942
if (const UnionType* utv = get<UnionType>(expectedType))
943
return returnFirstNonnullOptionOfType<FunctionType>(utv).has_value();
944
945
return false;
946
}
947
948
AutocompleteEntryMap autocompleteTypeNames(
949
const Module& module,
950
const ScopePtr& scopeAtPosition,
951
Position& position,
952
const std::vector<AstNode*>& ancestry
953
)
954
{
955
AutocompleteEntryMap result;
956
957
ScopePtr startScope = scopeAtPosition;
958
959
for (ScopePtr scope = startScope; scope; scope = scope->parent)
960
{
961
for (const auto& [name, ty] : scope->exportedTypeBindings)
962
{
963
if (!result.count(name))
964
result[name] = AutocompleteEntry{
965
AutocompleteEntryKind::Type,
966
ty.type,
967
false,
968
false,
969
TypeCorrectKind::None,
970
std::nullopt,
971
std::nullopt,
972
ty.type->documentationSymbol
973
};
974
}
975
976
for (const auto& [name, ty] : scope->privateTypeBindings)
977
{
978
if (!result.count(name))
979
result[name] = AutocompleteEntry{
980
AutocompleteEntryKind::Type,
981
ty.type,
982
false,
983
false,
984
TypeCorrectKind::None,
985
std::nullopt,
986
std::nullopt,
987
ty.type->documentationSymbol
988
};
989
}
990
991
for (const auto& [name, _] : scope->importedTypeBindings)
992
{
993
if (auto binding = scope->linearSearchForBinding(name, true))
994
{
995
if (!result.count(name))
996
result[name] = AutocompleteEntry{AutocompleteEntryKind::Module, binding->typeId};
997
}
998
}
999
}
1000
1001
AstNode* parent = nullptr;
1002
AstType* topType = nullptr; // TODO: rename?
1003
1004
for (auto it = ancestry.rbegin(), e = ancestry.rend(); it != e; ++it)
1005
{
1006
if (AstType* asType = (*it)->asType())
1007
{
1008
topType = asType;
1009
}
1010
else
1011
{
1012
parent = *it;
1013
break;
1014
}
1015
}
1016
1017
if (!parent)
1018
return result;
1019
1020
if (AstStatLocal* node = parent->as<AstStatLocal>()) // Try to provide inferred type of the local
1021
{
1022
// Look at which of the variable types we are defining
1023
for (size_t i = 0; i < node->vars.size; i++)
1024
{
1025
AstLocal* var = node->vars.data[i];
1026
1027
if (var->annotation && var->annotation->location.containsClosed(position))
1028
{
1029
if (node->values.size == 0)
1030
break;
1031
1032
unsigned tailPos = 0;
1033
1034
// For multiple return values we will try to unpack last function call return type pack
1035
if (i >= node->values.size)
1036
{
1037
tailPos = int(i) - int(node->values.size) + 1;
1038
i = int(node->values.size) - 1;
1039
}
1040
1041
AstExpr* expr = node->values.data[i]->asExpr();
1042
1043
if (!expr)
1044
break;
1045
1046
TypeId inferredType = nullptr;
1047
1048
if (AstExprCall* exprCall = expr->as<AstExprCall>())
1049
{
1050
if (auto it = module.astTypes.find(exprCall->func))
1051
{
1052
if (const FunctionType* ftv = get<FunctionType>(follow(*it)))
1053
{
1054
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, tailPos))
1055
inferredType = *ty;
1056
}
1057
}
1058
}
1059
else
1060
{
1061
if (tailPos != 0)
1062
break;
1063
1064
if (auto it = module.astTypes.find(expr))
1065
inferredType = *it;
1066
}
1067
1068
if (inferredType)
1069
tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, inferredType, position);
1070
1071
break;
1072
}
1073
}
1074
}
1075
else if (AstExprFunction* node = parent->as<AstExprFunction>())
1076
{
1077
// For lookup inside expected function type if that's available
1078
auto tryGetExpectedFunctionType = [](const Module& module, AstExpr* expr) -> const FunctionType*
1079
{
1080
auto it = module.astExpectedTypes.find(expr);
1081
1082
if (!it)
1083
return nullptr;
1084
1085
TypeId ty = follow(*it);
1086
1087
if (const FunctionType* ftv = get<FunctionType>(ty))
1088
return ftv;
1089
1090
// Handle optional function type
1091
if (const UnionType* utv = get<UnionType>(ty))
1092
{
1093
return returnFirstNonnullOptionOfType<FunctionType>(utv).value_or(nullptr);
1094
}
1095
1096
return nullptr;
1097
};
1098
1099
// Find which argument type we are defining
1100
for (size_t i = 0; i < node->args.size; i++)
1101
{
1102
AstLocal* arg = node->args.data[i];
1103
1104
if (arg->annotation && arg->annotation->location.containsClosed(position))
1105
{
1106
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
1107
{
1108
if (auto ty = tryGetTypePackTypeAt(ftv->argTypes, i))
1109
tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position);
1110
}
1111
// Otherwise, try to use the type inferred by typechecker
1112
else if (auto inferredType = getLocalTypeInScopeAt(module, scopeAtPosition, position, arg))
1113
{
1114
tryAddTypeCorrectSuggestion(result, startScope, topType, *inferredType, position);
1115
}
1116
1117
break;
1118
}
1119
}
1120
1121
if (AstTypePack* argTp = node->varargAnnotation)
1122
{
1123
if (auto variadic = argTp->as<AstTypePackVariadic>())
1124
{
1125
if (variadic->location.containsClosed(position))
1126
{
1127
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
1128
{
1129
if (auto ty = tryGetTypePackTypeAt(ftv->argTypes, ~0u))
1130
tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position);
1131
}
1132
}
1133
}
1134
}
1135
1136
if (!node->returnAnnotation)
1137
return result;
1138
1139
if (const auto typePack = node->returnAnnotation->as<AstTypePackExplicit>())
1140
{
1141
for (size_t i = 0; i < typePack->typeList.types.size; i++)
1142
{
1143
AstType* ret = typePack->typeList.types.data[i];
1144
1145
if (ret->location.containsClosed(position))
1146
{
1147
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
1148
{
1149
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, i))
1150
tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position);
1151
}
1152
1153
// TODO: with additional type information, we could suggest inferred return type here
1154
break;
1155
}
1156
}
1157
1158
if (AstTypePack* retTp = typePack->typeList.tailType)
1159
{
1160
if (auto variadic = retTp->as<AstTypePackVariadic>())
1161
{
1162
if (variadic->location.containsClosed(position))
1163
{
1164
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
1165
{
1166
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u))
1167
tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position);
1168
}
1169
}
1170
}
1171
}
1172
}
1173
else if (auto variadic = node->returnAnnotation->as<AstTypePackVariadic>())
1174
{
1175
if (variadic->location.containsClosed(position))
1176
{
1177
if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node))
1178
{
1179
if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u))
1180
tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position);
1181
}
1182
}
1183
}
1184
}
1185
1186
return result;
1187
}
1188
1189
static bool isInLocalNames(const std::vector<AstNode*>& ancestry, Position position)
1190
{
1191
for (auto iter = ancestry.rbegin(); iter != ancestry.rend(); iter++)
1192
{
1193
if (auto statLocal = (*iter)->as<AstStatLocal>())
1194
{
1195
for (auto var : statLocal->vars)
1196
{
1197
if (var->location.containsClosed(position))
1198
{
1199
return true;
1200
}
1201
}
1202
}
1203
else if (auto funcExpr = (*iter)->as<AstExprFunction>())
1204
{
1205
if (funcExpr->argLocation && funcExpr->argLocation->contains(position))
1206
{
1207
return true;
1208
}
1209
}
1210
else if (auto localFunc = (*iter)->as<AstStatLocalFunction>())
1211
{
1212
return localFunc->name->location.containsClosed(position);
1213
}
1214
else if (auto block = (*iter)->as<AstStatBlock>())
1215
{
1216
if (block->body.size > 0)
1217
{
1218
return false;
1219
}
1220
}
1221
else if ((*iter)->asStat())
1222
{
1223
return false;
1224
}
1225
}
1226
return false;
1227
}
1228
1229
static bool isIdentifier(AstNode* node)
1230
{
1231
return node->is<AstExprGlobal>() || node->is<AstExprLocal>();
1232
}
1233
1234
static bool isBeingDefined(const std::vector<AstNode*>& ancestry, const Symbol& symbol)
1235
{
1236
// Current set of rules only check for local binding match
1237
if (!symbol.local)
1238
return false;
1239
1240
for (auto iter = ancestry.rbegin(); iter != ancestry.rend(); iter++)
1241
{
1242
if (auto statLocal = (*iter)->as<AstStatLocal>())
1243
{
1244
for (auto var : statLocal->vars)
1245
{
1246
if (symbol.local == var)
1247
return true;
1248
}
1249
}
1250
}
1251
1252
return false;
1253
}
1254
1255
template<typename T>
1256
T* extractStat(const std::vector<AstNode*>& ancestry)
1257
{
1258
AstNode* node = ancestry.size() >= 1 ? ancestry.rbegin()[0] : nullptr;
1259
if (!node)
1260
return nullptr;
1261
1262
if (T* t = node->as<T>())
1263
return t;
1264
1265
AstNode* parent = ancestry.size() >= 2 ? ancestry.rbegin()[1] : nullptr;
1266
if (!parent)
1267
return nullptr;
1268
1269
AstNode* grandParent = ancestry.size() >= 3 ? ancestry.rbegin()[2] : nullptr;
1270
AstNode* greatGrandParent = ancestry.size() >= 4 ? ancestry.rbegin()[3] : nullptr;
1271
1272
if (!grandParent)
1273
return nullptr;
1274
1275
if (T* t = parent->as<T>(); t && grandParent->is<AstStatBlock>())
1276
return t;
1277
1278
if (!greatGrandParent)
1279
return nullptr;
1280
1281
if (T* t = greatGrandParent->as<T>(); t && grandParent->is<AstStatBlock>() && parent->is<AstStatError>() && isIdentifier(node))
1282
return t;
1283
1284
return nullptr;
1285
}
1286
1287
static bool isBindingLegalAtCurrentPosition(const Symbol& symbol, const Binding& binding, Position pos)
1288
{
1289
if (symbol.local)
1290
return binding.location.end < pos;
1291
1292
// Builtin globals have an empty location; for defined globals, we want pos to be outside of the definition range to suggest it
1293
return binding.location == Location() || !binding.location.containsClosed(pos);
1294
}
1295
1296
static bool isValidBreakContinueContext(const std::vector<AstNode*>& ancestry, Position position)
1297
{
1298
for (auto it = ancestry.rbegin(); it != ancestry.rend(); ++it)
1299
{
1300
if ((*it)->is<AstStatFunction>() || (*it)->is<AstStatLocalFunction>() || (*it)->is<AstExprFunction>() || (*it)->is<AstStatTypeFunction>() ||
1301
(*it)->is<AstTypeFunction>())
1302
return false;
1303
1304
if (auto statWhile = (*it)->as<AstStatWhile>(); statWhile && statWhile->body->location.contains(position))
1305
return true;
1306
else if (auto statFor = (*it)->as<AstStatFor>(); statFor && statFor->body->location.contains(position))
1307
return true;
1308
else if (auto statForIn = (*it)->as<AstStatForIn>(); statForIn && statForIn->body->location.contains(position))
1309
return true;
1310
else if (auto statRepeat = (*it)->as<AstStatRepeat>(); statRepeat && statRepeat->body->location.contains(position))
1311
return true;
1312
}
1313
1314
return false;
1315
}
1316
1317
static AutocompleteEntryMap autocompleteStatement(
1318
const Module& module,
1319
const std::vector<AstNode*>& ancestry,
1320
const ScopePtr& scopeAtPosition,
1321
Position& position
1322
)
1323
{
1324
// This is inefficient. :(
1325
ScopePtr scope = scopeAtPosition;
1326
1327
AutocompleteEntryMap result;
1328
1329
if (isInLocalNames(ancestry, position))
1330
{
1331
autocompleteKeywords(ancestry, position, result);
1332
return result;
1333
}
1334
1335
while (scope)
1336
{
1337
for (const auto& [name, binding] : scope->bindings)
1338
{
1339
if (!isBindingLegalAtCurrentPosition(name, binding, position))
1340
continue;
1341
1342
std::string n = toString(name);
1343
if (!result.count(n))
1344
result[n] = {
1345
AutocompleteEntryKind::Binding,
1346
binding.typeId,
1347
binding.deprecated,
1348
false,
1349
TypeCorrectKind::None,
1350
std::nullopt,
1351
std::nullopt,
1352
binding.documentationSymbol,
1353
{},
1354
getParenRecommendation(binding.typeId, ancestry, TypeCorrectKind::None)
1355
};
1356
}
1357
1358
scope = scope->parent;
1359
}
1360
1361
bool shouldIncludeBreakAndContinue = isValidBreakContinueContext(ancestry, position);
1362
for (const std::string_view kw : kStatementStartingKeywords)
1363
{
1364
if ((kw != "break" && kw != "continue") || shouldIncludeBreakAndContinue)
1365
result.emplace(kw, AutocompleteEntry{AutocompleteEntryKind::Keyword});
1366
}
1367
1368
for (auto it = ancestry.rbegin(); it != ancestry.rend(); ++it)
1369
{
1370
if (AstStatForIn* statForIn = (*it)->as<AstStatForIn>(); statForIn && !statForIn->body->hasEnd)
1371
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1372
else if (AstStatFor* statFor = (*it)->as<AstStatFor>(); statFor && !statFor->body->hasEnd)
1373
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1374
else if (AstStatIf* statIf = (*it)->as<AstStatIf>())
1375
{
1376
bool hasEnd = statIf->thenbody->hasEnd;
1377
if (statIf->elsebody)
1378
{
1379
if (AstStatBlock* elseBlock = statIf->elsebody->as<AstStatBlock>())
1380
hasEnd = elseBlock->hasEnd;
1381
}
1382
1383
if (!hasEnd)
1384
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1385
}
1386
else if (AstStatWhile* statWhile = (*it)->as<AstStatWhile>(); statWhile && !statWhile->body->hasEnd)
1387
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1388
else if (AstExprFunction* exprFunction = (*it)->as<AstExprFunction>(); exprFunction && !exprFunction->body->hasEnd)
1389
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1390
if (AstStatBlock* exprBlock = (*it)->as<AstStatBlock>(); exprBlock && !exprBlock->hasEnd)
1391
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1392
}
1393
1394
if (ancestry.size() >= 2)
1395
{
1396
AstNode* parent = ancestry.rbegin()[1];
1397
if (AstStatIf* statIf = parent->as<AstStatIf>())
1398
{
1399
if (!statIf->elsebody || (statIf->elseLocation && statIf->elseLocation->containsClosed(position)))
1400
{
1401
result.emplace("else", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1402
result.emplace("elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1403
}
1404
}
1405
1406
if (AstStatRepeat* statRepeat = parent->as<AstStatRepeat>(); statRepeat && !statRepeat->body->hasEnd)
1407
result.emplace("until", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1408
}
1409
1410
if (ancestry.size() >= 4)
1411
{
1412
auto iter = ancestry.rbegin();
1413
if (AstStatIf* statIf = iter[3]->as<AstStatIf>();
1414
statIf != nullptr && !statIf->elsebody && iter[2]->is<AstStatBlock>() && iter[1]->is<AstStatError>() && isIdentifier(iter[0]))
1415
{
1416
result.emplace("else", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1417
result.emplace("elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1418
}
1419
}
1420
1421
if (AstStatRepeat* statRepeat = extractStat<AstStatRepeat>(ancestry); statRepeat && !statRepeat->body->hasEnd)
1422
result.emplace("until", AutocompleteEntry{AutocompleteEntryKind::Keyword});
1423
1424
return result;
1425
}
1426
1427
// Returns true iff `node` was handled by this function (completions, if any, are returned in `outResult`)
1428
static bool autocompleteIfElseExpression(
1429
const AstNode* node,
1430
const std::vector<AstNode*>& ancestry,
1431
const Position& position,
1432
AutocompleteEntryMap& outResult
1433
)
1434
{
1435
AstNode* parent = ancestry.size() >= 2 ? ancestry.rbegin()[1] : nullptr;
1436
if (!parent)
1437
return false;
1438
1439
if (node->is<AstExprIfElse>())
1440
{
1441
// Don't try to complete when the current node is an if-else expression (i.e. only try to complete when the node is a child of an if-else
1442
// expression).
1443
return true;
1444
}
1445
1446
AstExprIfElse* ifElseExpr = parent->as<AstExprIfElse>();
1447
if (!ifElseExpr || ifElseExpr->condition->location.containsClosed(position))
1448
{
1449
return false;
1450
}
1451
else if (!ifElseExpr->hasThen)
1452
{
1453
outResult["then"] = {AutocompleteEntryKind::Keyword};
1454
return true;
1455
}
1456
else if (ifElseExpr->trueExpr->location.containsClosed(position))
1457
{
1458
return false;
1459
}
1460
else if (!ifElseExpr->hasElse)
1461
{
1462
outResult["else"] = {AutocompleteEntryKind::Keyword};
1463
outResult["elseif"] = {AutocompleteEntryKind::Keyword};
1464
return true;
1465
}
1466
else
1467
{
1468
return false;
1469
}
1470
}
1471
1472
static AutocompleteContext autocompleteExpression(
1473
const Module& module,
1474
NotNull<BuiltinTypes> builtinTypes,
1475
TypeArena* typeArena,
1476
const std::vector<AstNode*>& ancestry,
1477
const ScopePtr& scopeAtPosition,
1478
Position position,
1479
AutocompleteEntryMap& result
1480
)
1481
{
1482
LUAU_ASSERT(!ancestry.empty());
1483
1484
AstNode* node = ancestry.rbegin()[0];
1485
1486
if (FFlag::DebugLuauMagicVariableNames)
1487
{
1488
InternalErrorReporter ice;
1489
if (auto local = node->as<AstExprLocal>(); local && local->local->name == "_luau_autocomplete_ice")
1490
ice.ice("_luau_autocomplete_ice encountered", local->location);
1491
if (auto global = node->as<AstExprGlobal>(); global && global->name == "_luau_autocomplete_ice")
1492
ice.ice("_luau_autocomplete_ice encountered", global->location);
1493
}
1494
1495
if (node->is<AstExprIndexName>())
1496
{
1497
if (auto it = module.astTypes.find(node->asExpr()))
1498
autocompleteProps(module, typeArena, builtinTypes, *it, PropIndexType::Point, ancestry, result);
1499
}
1500
else if (autocompleteIfElseExpression(node, ancestry, position, result))
1501
return AutocompleteContext::Keyword;
1502
else if (node->is<AstExprFunction>())
1503
return AutocompleteContext::Unknown;
1504
else
1505
{
1506
// This is inefficient. :(
1507
ScopePtr scope = scopeAtPosition;
1508
1509
while (scope)
1510
{
1511
for (const auto& [name, binding] : scope->bindings)
1512
{
1513
if (!isBindingLegalAtCurrentPosition(name, binding, position))
1514
continue;
1515
1516
if (isBeingDefined(ancestry, name))
1517
continue;
1518
1519
std::string n = toString(name);
1520
if (!result.count(n))
1521
{
1522
TypeCorrectKind typeCorrect = checkTypeCorrectKind(module, typeArena, builtinTypes, node, position, binding.typeId);
1523
1524
result[n] = {
1525
AutocompleteEntryKind::Binding,
1526
binding.typeId,
1527
binding.deprecated,
1528
false,
1529
typeCorrect,
1530
std::nullopt,
1531
std::nullopt,
1532
binding.documentationSymbol,
1533
{},
1534
getParenRecommendation(binding.typeId, ancestry, typeCorrect)
1535
};
1536
}
1537
}
1538
1539
scope = scope->parent;
1540
}
1541
1542
TypeCorrectKind correctForNil = checkTypeCorrectKind(module, typeArena, builtinTypes, node, position, builtinTypes->nilType);
1543
TypeCorrectKind correctForTrue = checkTypeCorrectKind(module, typeArena, builtinTypes, node, position, builtinTypes->trueType);
1544
TypeCorrectKind correctForFalse = checkTypeCorrectKind(module, typeArena, builtinTypes, node, position, builtinTypes->falseType);
1545
TypeCorrectKind correctForFunction =
1546
functionIsExpectedAt(module, node, position).value_or(false) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
1547
1548
result["if"] = {AutocompleteEntryKind::Keyword, std::nullopt, false, false};
1549
result["true"] = {AutocompleteEntryKind::Keyword, builtinTypes->booleanType, false, false, correctForTrue};
1550
result["false"] = {AutocompleteEntryKind::Keyword, builtinTypes->booleanType, false, false, correctForFalse};
1551
result["nil"] = {AutocompleteEntryKind::Keyword, builtinTypes->nilType, false, false, correctForNil};
1552
result["not"] = {AutocompleteEntryKind::Keyword};
1553
result["function"] = {AutocompleteEntryKind::Keyword, std::nullopt, false, false, correctForFunction};
1554
1555
if (auto ty = findExpectedTypeAt(module, node, position))
1556
autocompleteStringSingleton(*ty, true, node, position, result);
1557
}
1558
1559
return AutocompleteContext::Expression;
1560
}
1561
1562
static AutocompleteResult autocompleteExpression(
1563
const Module& module,
1564
NotNull<BuiltinTypes> builtinTypes,
1565
TypeArena* typeArena,
1566
const std::vector<AstNode*>& ancestry,
1567
const ScopePtr& scopeAtPosition,
1568
Position position
1569
)
1570
{
1571
AutocompleteEntryMap result;
1572
AutocompleteContext context = autocompleteExpression(module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result);
1573
return {std::move(result), ancestry, context};
1574
}
1575
1576
static std::optional<const ExternType*> getMethodContainingExternType(const ModulePtr& module, AstExpr* funcExpr)
1577
{
1578
AstExpr* parentExpr = nullptr;
1579
if (auto indexName = funcExpr->as<AstExprIndexName>())
1580
{
1581
parentExpr = indexName->expr;
1582
}
1583
else if (auto indexExpr = funcExpr->as<AstExprIndexExpr>())
1584
{
1585
parentExpr = indexExpr->expr;
1586
}
1587
else
1588
{
1589
return std::nullopt;
1590
}
1591
1592
auto parentIt = module->astTypes.find(parentExpr);
1593
if (!parentIt)
1594
{
1595
return std::nullopt;
1596
}
1597
1598
Luau::TypeId parentType = Luau::follow(*parentIt);
1599
1600
if (auto parentExternType = Luau::get<ExternType>(parentType))
1601
{
1602
return parentExternType;
1603
}
1604
1605
if (auto parentUnion = Luau::get<UnionType>(parentType))
1606
{
1607
return returnFirstNonnullOptionOfType<ExternType>(parentUnion);
1608
}
1609
1610
return std::nullopt;
1611
}
1612
1613
static bool stringPartOfInterpString(const AstNode* node, Position position)
1614
{
1615
const AstExprInterpString* interpString = node->as<AstExprInterpString>();
1616
if (!interpString)
1617
{
1618
return false;
1619
}
1620
1621
for (const AstExpr* expression : interpString->expressions)
1622
{
1623
if (expression->location.containsClosed(position))
1624
{
1625
return false;
1626
}
1627
}
1628
1629
return true;
1630
}
1631
1632
static bool isSimpleInterpolatedString(const AstNode* node)
1633
{
1634
const AstExprInterpString* interpString = node->as<AstExprInterpString>();
1635
return interpString != nullptr && interpString->expressions.size == 0;
1636
}
1637
1638
static std::optional<std::string> getStringContents(const AstNode* node)
1639
{
1640
if (const AstExprConstantString* string = node->as<AstExprConstantString>())
1641
{
1642
return std::string(string->value.data, string->value.size);
1643
}
1644
else if (const AstExprInterpString* interpString = node->as<AstExprInterpString>(); interpString && interpString->expressions.size == 0)
1645
{
1646
LUAU_ASSERT(interpString->strings.size == 1);
1647
return std::string(interpString->strings.data->data, interpString->strings.data->size);
1648
}
1649
else
1650
{
1651
return std::nullopt;
1652
}
1653
}
1654
1655
static std::optional<AutocompleteEntryMap> convertRequireSuggestionsToAutocompleteEntryMap(std::optional<RequireSuggestions> suggestions)
1656
{
1657
if (!suggestions)
1658
return std::nullopt;
1659
1660
AutocompleteEntryMap result;
1661
for (RequireSuggestion& suggestion : *suggestions)
1662
{
1663
AutocompleteEntry entry = {AutocompleteEntryKind::RequirePath};
1664
entry.insertText = std::move(suggestion.fullPath);
1665
entry.tags = std::move(suggestion.tags);
1666
result[std::move(suggestion.label)] = std::move(entry);
1667
}
1668
return result;
1669
}
1670
1671
static std::optional<AutocompleteEntryMap> autocompleteStringParams(
1672
const ModulePtr& module,
1673
const std::vector<AstNode*>& nodes,
1674
Position position,
1675
FileResolver* fileResolver,
1676
StringCompletionCallback callback
1677
)
1678
{
1679
if (nodes.size() < 2)
1680
{
1681
return std::nullopt;
1682
}
1683
1684
if (!nodes.back()->is<AstExprConstantString>() && !isSimpleInterpolatedString(nodes.back()) && !nodes.back()->is<AstExprError>())
1685
{
1686
return std::nullopt;
1687
}
1688
1689
if (!nodes.back()->is<AstExprError>())
1690
{
1691
if (nodes.back()->location.end == position || nodes.back()->location.begin == position)
1692
{
1693
return std::nullopt;
1694
}
1695
}
1696
1697
AstExprCall* candidate = nodes.at(nodes.size() - 2)->as<AstExprCall>();
1698
if (!candidate)
1699
{
1700
return std::nullopt;
1701
}
1702
1703
// HACK: All current instances of 'magic string' params are the first parameter of their functions,
1704
// so we encode that here rather than putting a useless member on the FunctionType struct.
1705
if (candidate->args.size > 1 && !candidate->args.data[0]->location.contains(position))
1706
{
1707
return std::nullopt;
1708
}
1709
1710
auto it = module->astTypes.find(candidate->func);
1711
if (!it)
1712
{
1713
return std::nullopt;
1714
}
1715
1716
std::optional<std::string> candidateString = getStringContents(nodes.back());
1717
1718
auto performCallback = [&](const FunctionType* funcType) -> std::optional<AutocompleteEntryMap>
1719
{
1720
for (const std::string& tag : funcType->tags)
1721
{
1722
if (tag == kRequireTagName && fileResolver)
1723
{
1724
return convertRequireSuggestionsToAutocompleteEntryMap(fileResolver->getRequireSuggestions(module->name, candidateString));
1725
}
1726
if (std::optional<AutocompleteEntryMap> ret = callback(tag, getMethodContainingExternType(module, candidate->func), candidateString))
1727
{
1728
return ret;
1729
}
1730
}
1731
return std::nullopt;
1732
};
1733
1734
auto followedId = Luau::follow(*it);
1735
if (auto functionType = Luau::get<FunctionType>(followedId))
1736
{
1737
return performCallback(functionType);
1738
}
1739
1740
if (auto intersect = Luau::get<IntersectionType>(followedId))
1741
{
1742
for (TypeId part : intersect->parts)
1743
{
1744
part = follow(part);
1745
if (auto candidateFunctionType = Luau::get<FunctionType>(part))
1746
{
1747
if (std::optional<AutocompleteEntryMap> ret = performCallback(candidateFunctionType))
1748
{
1749
return ret;
1750
}
1751
}
1752
}
1753
}
1754
1755
return std::nullopt;
1756
}
1757
1758
static AutocompleteResult autocompleteWhileLoopKeywords(std::vector<AstNode*> ancestry)
1759
{
1760
AutocompleteEntryMap ret;
1761
ret["do"] = {AutocompleteEntryKind::Keyword};
1762
ret["and"] = {AutocompleteEntryKind::Keyword};
1763
ret["or"] = {AutocompleteEntryKind::Keyword};
1764
return {std::move(ret), std::move(ancestry), AutocompleteContext::Keyword};
1765
}
1766
1767
static std::string makeAnonymous(const ScopePtr& scope, const FunctionType& funcTy)
1768
{
1769
std::string result = "function(";
1770
1771
auto [args, tail] = Luau::flatten(funcTy.argTypes);
1772
1773
bool first = true;
1774
// Skip the implicit 'self' argument if call is indexed with ':'
1775
for (size_t argIdx = 0; argIdx < args.size(); ++argIdx)
1776
{
1777
if (!first)
1778
result += ", ";
1779
else
1780
first = false;
1781
1782
std::string name;
1783
if (argIdx < funcTy.argNames.size() && funcTy.argNames[argIdx])
1784
name = funcTy.argNames[argIdx]->name;
1785
else
1786
name = "a" + std::to_string(argIdx);
1787
1788
if (std::optional<Name> type = tryGetTypeNameInScope(scope, args[argIdx], true))
1789
result += name + ": " + *type;
1790
else
1791
result += name;
1792
}
1793
1794
if (tail && (Luau::isVariadic(*tail) || Luau::get<Luau::FreeTypePack>(Luau::follow(*tail))))
1795
{
1796
if (!first)
1797
result += ", ";
1798
1799
std::optional<std::string> varArgType;
1800
if (const VariadicTypePack* pack = get<VariadicTypePack>(follow(*tail)))
1801
{
1802
if (std::optional<std::string> res = tryGetTypeNameInScope(scope, pack->ty, true))
1803
varArgType = std::move(res);
1804
}
1805
1806
if (varArgType)
1807
result += "...: " + *varArgType;
1808
else
1809
result += "...";
1810
}
1811
1812
result += ")";
1813
1814
auto [rets, retTail] = Luau::flatten(funcTy.retTypes);
1815
if (const size_t totalRetSize = rets.size() + (retTail ? 1 : 0); totalRetSize > 0)
1816
{
1817
if (std::optional<std::string> returnTypes = tryGetTypeNameInScope(scope, funcTy.retTypes, true))
1818
{
1819
result += ": ";
1820
bool wrap = totalRetSize != 1;
1821
if (wrap)
1822
result += "(";
1823
result += *returnTypes;
1824
if (wrap)
1825
result += ")";
1826
}
1827
}
1828
result += " end";
1829
return result;
1830
}
1831
1832
static std::optional<AutocompleteEntry> makeAnonymousAutofilled(
1833
const ModulePtr& module,
1834
const ScopePtr& scopeAtPosition,
1835
Position position,
1836
const AstNode* node,
1837
const std::vector<AstNode*>& ancestry
1838
)
1839
{
1840
const AstExprCall* call = node->as<AstExprCall>();
1841
if (!call && ancestry.size() > 1)
1842
call = ancestry[ancestry.size() - 2]->as<AstExprCall>();
1843
1844
if (!call)
1845
return std::nullopt;
1846
1847
if (!call->location.containsClosed(position) || call->func->location.containsClosed(position))
1848
return std::nullopt;
1849
1850
TypeId* typeIter = module->astTypes.find(call->func);
1851
if (!typeIter)
1852
return std::nullopt;
1853
1854
const FunctionType* outerFunction = get<FunctionType>(follow(*typeIter));
1855
if (!outerFunction)
1856
return std::nullopt;
1857
1858
size_t argument = 0;
1859
for (size_t i = 0; i < call->args.size; ++i)
1860
{
1861
if (call->args.data[i]->location.containsClosed(position))
1862
{
1863
argument = i;
1864
break;
1865
}
1866
}
1867
1868
if (call->self)
1869
argument++;
1870
1871
std::optional<TypeId> argType;
1872
auto [args, tail] = flatten(outerFunction->argTypes);
1873
if (argument < args.size())
1874
argType = args[argument];
1875
1876
if (!argType)
1877
return std::nullopt;
1878
1879
TypeId followed = follow(*argType);
1880
const FunctionType* type = get<FunctionType>(followed);
1881
if (!type)
1882
{
1883
if (const UnionType* unionType = get<UnionType>(followed))
1884
{
1885
if (std::optional<const FunctionType*> nonnullFunction = returnFirstNonnullOptionOfType<FunctionType>(unionType))
1886
type = *nonnullFunction;
1887
}
1888
}
1889
1890
if (!type)
1891
return std::nullopt;
1892
1893
const ScopePtr scope = scopeAtPosition;
1894
if (!scope)
1895
return std::nullopt;
1896
1897
AutocompleteEntry entry;
1898
entry.kind = AutocompleteEntryKind::GeneratedFunction;
1899
entry.typeCorrect = TypeCorrectKind::Correct;
1900
entry.type = argType;
1901
entry.insertText = makeAnonymous(scope, *type);
1902
return std::make_optional(std::move(entry));
1903
}
1904
1905
AutocompleteResult autocomplete_(
1906
const ModulePtr& module,
1907
NotNull<BuiltinTypes> builtinTypes,
1908
TypeArena* typeArena,
1909
std::vector<AstNode*>& ancestry,
1910
Scope* globalScope, // [TODO] This is unused argument, do we really need this?
1911
const ScopePtr& scopeAtPosition,
1912
Position position,
1913
FileResolver* fileResolver,
1914
StringCompletionCallback callback,
1915
bool isInHotComment
1916
)
1917
{
1918
LUAU_TIMETRACE_SCOPE("Luau::autocomplete_", "AutocompleteCore");
1919
1920
if (isInHotComment)
1921
{
1922
AutocompleteEntryMap result;
1923
1924
for (const std::string_view hc : kHotComments)
1925
result.emplace(hc, AutocompleteEntry{AutocompleteEntryKind::HotComment});
1926
return {std::move(result), ancestry, AutocompleteContext::HotComment};
1927
}
1928
1929
AstNode* node = ancestry.back();
1930
1931
AstExprConstantNil dummy{Location{}};
1932
AstNode* parent = ancestry.size() >= 2 ? ancestry.rbegin()[1] : &dummy;
1933
1934
// If we are inside a body of a function that doesn't have a completed argument list, ignore the body node
1935
if (auto exprFunction = parent->as<AstExprFunction>(); exprFunction && !exprFunction->argLocation && node == exprFunction->body)
1936
{
1937
ancestry.pop_back();
1938
1939
node = ancestry.back();
1940
parent = ancestry.size() >= 2 ? ancestry.rbegin()[1] : &dummy;
1941
}
1942
1943
if (auto indexName = node->as<AstExprIndexName>())
1944
{
1945
auto it = module->astTypes.find(indexName->expr);
1946
if (!it)
1947
return {};
1948
1949
TypeId ty = follow(*it);
1950
PropIndexType indexType = indexName->op == ':' ? PropIndexType::Colon : PropIndexType::Point;
1951
1952
return {autocompleteProps(*module, typeArena, builtinTypes, ty, indexType, ancestry), ancestry, AutocompleteContext::Property};
1953
}
1954
else if (auto typeReference = node->as<AstTypeReference>())
1955
{
1956
if (typeReference->prefix)
1957
return {autocompleteModuleTypes(*module, scopeAtPosition, position, typeReference->prefix->value), ancestry, AutocompleteContext::Type};
1958
else
1959
return {autocompleteTypeNames(*module, scopeAtPosition, position, ancestry), ancestry, AutocompleteContext::Type};
1960
}
1961
else if (node->is<AstTypeError>())
1962
{
1963
return {autocompleteTypeNames(*module, scopeAtPosition, position, ancestry), ancestry, AutocompleteContext::Type};
1964
}
1965
else if (AstStatLocal* statLocal = node->as<AstStatLocal>())
1966
{
1967
if (statLocal->vars.size == 1 && (!statLocal->equalsSignLocation || position < statLocal->equalsSignLocation->begin))
1968
return {{{"function", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Unknown};
1969
else if (statLocal->equalsSignLocation && position >= statLocal->equalsSignLocation->end)
1970
return autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position);
1971
else
1972
return {};
1973
}
1974
1975
else if (AstStatFor* statFor = extractStat<AstStatFor>(ancestry))
1976
{
1977
if (!statFor->hasDo || position < statFor->doLocation.begin)
1978
{
1979
if (statFor->from->location.containsClosed(position) || statFor->to->location.containsClosed(position) ||
1980
(statFor->step && statFor->step->location.containsClosed(position)))
1981
return autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position);
1982
1983
if (!statFor->from->is<AstExprError>() && !statFor->to->is<AstExprError>() && (!statFor->step || !statFor->step->is<AstExprError>()))
1984
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword};
1985
return {};
1986
}
1987
1988
return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement};
1989
}
1990
1991
else if (AstStatForIn* statForIn = parent->as<AstStatForIn>(); statForIn && (node->is<AstStatBlock>() || isIdentifier(node)))
1992
{
1993
if (!statForIn->hasIn || position <= statForIn->inLocation.begin)
1994
{
1995
AstLocal* lastName = statForIn->vars.data[statForIn->vars.size - 1];
1996
if (lastName->name == kParseNameError || lastName->location.containsClosed(position))
1997
{
1998
// Here we are either working with a missing binding (as would be the case in a bare "for" keyword) or
1999
// the cursor is still touching a binding name. The user is still typing a new name, so we should not offer
2000
// any suggestions.
2001
return {};
2002
}
2003
2004
return {{{"in", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword};
2005
}
2006
2007
if (!statForIn->hasDo || position <= statForIn->doLocation.begin)
2008
{
2009
LUAU_ASSERT(statForIn->values.size > 0);
2010
AstExpr* lastExpr = statForIn->values.data[statForIn->values.size - 1];
2011
2012
if (lastExpr->location.containsClosed(position))
2013
return autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position);
2014
2015
if (position > lastExpr->location.end)
2016
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword};
2017
2018
return {}; // Not sure what this means
2019
}
2020
}
2021
else if (AstStatForIn* statForIn = extractStat<AstStatForIn>(ancestry))
2022
{
2023
// The AST looks a bit differently if the cursor is at a position where only the "do" keyword is allowed.
2024
// ex "for f in f do"
2025
if (!statForIn->hasDo)
2026
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword};
2027
2028
return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement};
2029
}
2030
2031
else if (AstStatWhile* statWhile = parent->as<AstStatWhile>(); node->is<AstStatBlock>() && statWhile)
2032
{
2033
if (!statWhile->hasDo && !statWhile->condition->is<AstStatError>() && position > statWhile->condition->location.end)
2034
{
2035
return autocompleteWhileLoopKeywords(ancestry);
2036
}
2037
2038
if (!statWhile->hasDo || position < statWhile->doLocation.begin)
2039
return autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position);
2040
2041
if (statWhile->hasDo && position > statWhile->doLocation.end)
2042
return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement};
2043
}
2044
2045
else if (AstStatWhile* statWhile = extractStat<AstStatWhile>(ancestry);
2046
(statWhile && (!statWhile->hasDo || statWhile->doLocation.containsClosed(position)) && statWhile->condition &&
2047
!statWhile->condition->location.containsClosed(position)))
2048
{
2049
return autocompleteWhileLoopKeywords(ancestry);
2050
}
2051
else if (AstStatIf* statIf = node->as<AstStatIf>(); statIf && !statIf->elseLocation.has_value())
2052
{
2053
return {
2054
{{"else", AutocompleteEntry{AutocompleteEntryKind::Keyword}}, {"elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword}}},
2055
ancestry,
2056
AutocompleteContext::Keyword
2057
};
2058
}
2059
else if (AstStatIf* statIf = parent->as<AstStatIf>(); statIf && node->is<AstStatBlock>())
2060
{
2061
if (statIf->condition->is<AstExprError>())
2062
return autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position);
2063
else if (!statIf->thenLocation || statIf->thenLocation->containsClosed(position))
2064
return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry, AutocompleteContext::Keyword};
2065
}
2066
else if (AstStatIf* statIf = extractStat<AstStatIf>(ancestry); statIf &&
2067
(!statIf->thenLocation || statIf->thenLocation->containsClosed(position)) &&
2068
(statIf->condition && !statIf->condition->location.containsClosed(position)))
2069
{
2070
AutocompleteEntryMap ret;
2071
ret["then"] = {AutocompleteEntryKind::Keyword};
2072
ret["and"] = {AutocompleteEntryKind::Keyword};
2073
ret["or"] = {AutocompleteEntryKind::Keyword};
2074
return {std::move(ret), ancestry, AutocompleteContext::Keyword};
2075
}
2076
else if (AstStatRepeat* statRepeat = node->as<AstStatRepeat>(); statRepeat && statRepeat->condition->is<AstExprError>())
2077
return autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position);
2078
else if (AstStatRepeat* statRepeat = extractStat<AstStatRepeat>(ancestry); statRepeat)
2079
return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement};
2080
else if (AstExprTable* exprTable = parent->as<AstExprTable>();
2081
exprTable && (node->is<AstExprGlobal>() || node->is<AstExprConstantString>() || node->is<AstExprInterpString>()))
2082
{
2083
for (const auto& [kind, key, value] : exprTable->items)
2084
{
2085
// If item doesn't have a key, maybe the value is actually the key
2086
if (key ? key == node : node->is<AstExprGlobal>() && value == node)
2087
{
2088
if (auto it = module->astExpectedTypes.find(exprTable))
2089
{
2090
auto result = autocompleteProps(*module, typeArena, builtinTypes, *it, PropIndexType::Key, ancestry);
2091
2092
if (auto nodeIt = module->astExpectedTypes.find(node->asExpr()))
2093
autocompleteStringSingleton(*nodeIt, !node->is<AstExprConstantString>(), node, position, result);
2094
2095
if (!key)
2096
{
2097
// If there is "no key," it may be that the user
2098
// intends for the current token to be the key, but
2099
// has yet to type the `=` sign.
2100
//
2101
// If the key type is a union of singleton strings,
2102
// suggest those too.
2103
if (auto ttv = get<TableType>(follow(*it)); ttv && ttv->indexer)
2104
{
2105
autocompleteStringSingleton(ttv->indexer->indexType, false, node, position, result);
2106
}
2107
}
2108
2109
// Remove keys that are already completed
2110
for (const auto& item : exprTable->items)
2111
{
2112
if (!item.key)
2113
continue;
2114
2115
if (auto stringKey = item.key->as<AstExprConstantString>())
2116
result.erase(std::string(stringKey->value.data, stringKey->value.size));
2117
}
2118
2119
// If we know for sure that a key is being written, do not offer general expression suggestions
2120
if (!key)
2121
autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result);
2122
2123
return {std::move(result), ancestry, AutocompleteContext::Property};
2124
}
2125
2126
break;
2127
}
2128
}
2129
}
2130
else if (AstExprTable* exprTable = node->as<AstExprTable>())
2131
{
2132
AutocompleteEntryMap result;
2133
2134
if (auto it = module->astExpectedTypes.find(exprTable))
2135
{
2136
result = autocompleteProps(*module, typeArena, builtinTypes, *it, PropIndexType::Key, ancestry);
2137
2138
// If the key type is a union of singleton strings,
2139
// suggest those too.
2140
if (auto ttv = get<TableType>(follow(*it)); ttv && ttv->indexer)
2141
{
2142
autocompleteStringSingleton(ttv->indexer->indexType, false, node, position, result);
2143
}
2144
2145
// Remove keys that are already completed
2146
for (const auto& item : exprTable->items)
2147
{
2148
if (!item.key)
2149
continue;
2150
2151
if (auto stringKey = item.key->as<AstExprConstantString>())
2152
result.erase(std::string(stringKey->value.data, stringKey->value.size));
2153
}
2154
}
2155
2156
// Also offer general expression suggestions
2157
autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position, result);
2158
2159
return {std::move(result), ancestry, AutocompleteContext::Property};
2160
}
2161
else if (isIdentifier(node) && (parent->is<AstStatExpr>() || parent->is<AstStatError>()))
2162
return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement};
2163
2164
if (std::optional<AutocompleteEntryMap> ret = autocompleteStringParams(module, ancestry, position, fileResolver, std::move(callback)))
2165
{
2166
return {*ret, ancestry, AutocompleteContext::String};
2167
}
2168
else if (node->is<AstExprConstantString>() || isSimpleInterpolatedString(node))
2169
{
2170
AutocompleteEntryMap result;
2171
2172
if (ancestry.size() >= 2)
2173
{
2174
if (auto idxExpr = ancestry.at(ancestry.size() - 2)->as<AstExprIndexExpr>())
2175
{
2176
if (auto it = module->astTypes.find(idxExpr->expr))
2177
autocompleteProps(*module, typeArena, builtinTypes, follow(*it), PropIndexType::Point, ancestry, result);
2178
}
2179
else if (auto binExpr = ancestry.at(ancestry.size() - 2)->as<AstExprBinary>())
2180
{
2181
if (binExpr->op == AstExprBinary::CompareEq || binExpr->op == AstExprBinary::CompareNe)
2182
{
2183
if (auto it = module->astTypes.find(node == binExpr->left ? binExpr->right : binExpr->left))
2184
autocompleteStringSingleton(*it, false, node, position, result);
2185
}
2186
}
2187
}
2188
2189
if (auto it = module->astExpectedTypes.find(node->asExpr()))
2190
autocompleteStringSingleton(*it, false, node, position, result);
2191
2192
return {std::move(result), ancestry, AutocompleteContext::String};
2193
}
2194
else if (stringPartOfInterpString(node, position))
2195
{
2196
// We're not a simple interpolated string, we're something like `a{"b"}@1`, and we
2197
// can't know what to format to
2198
AutocompleteEntryMap map;
2199
return {std::move(map), ancestry, AutocompleteContext::String};
2200
}
2201
else if (AstExprFunction* func = node->as<AstExprFunction>())
2202
{
2203
for (AstAttr* attr : func->attributes)
2204
{
2205
if (attr->location.begin <= position && position <= attr->location.end && attr->type == AstAttr::Type::Unknown)
2206
{
2207
AutocompleteEntryMap ret;
2208
for (const auto& attr : kKnownAttributes)
2209
ret[attr.c_str()] = {AutocompleteEntryKind::Keyword};
2210
return {std::move(ret), std::move(ancestry), AutocompleteContext::Keyword};
2211
}
2212
}
2213
}
2214
2215
if (node->is<AstExprConstantNumber>())
2216
return {};
2217
2218
if (node->asExpr())
2219
{
2220
AutocompleteResult ret = autocompleteExpression(*module, builtinTypes, typeArena, ancestry, scopeAtPosition, position);
2221
if (std::optional<AutocompleteEntry> generated = makeAnonymousAutofilled(module, scopeAtPosition, position, node, ancestry))
2222
ret.entryMap[kGeneratedAnonymousFunctionEntryName] = std::move(*generated);
2223
return ret;
2224
}
2225
else if (node->asStat())
2226
return {autocompleteStatement(*module, ancestry, scopeAtPosition, position), ancestry, AutocompleteContext::Statement};
2227
2228
return {};
2229
}
2230
2231
} // namespace Luau
2232
2233