luau/tests/FragmentAutocomplete.test.cpp

1387 lines
36 KiB
C++
Raw Permalink Normal View History

Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/FragmentAutocomplete.h"
#include "Fixture.h"
#include "Luau/Ast.h"
#include "Luau/AstQuery.h"
#include "Luau/Autocomplete.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Common.h"
2024-10-25 21:15:01 +01:00
#include "Luau/Frontend.h"
#include "Luau/AutocompleteTypes.h"
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
#include <algorithm>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <optional>
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
using namespace Luau;
LUAU_FASTFLAG(LuauAllowFragmentParsing);
LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete)
LUAU_FASTFLAG(LuauSymbolEquality);
LUAU_FASTFLAG(LuauStoreSolverTypeOnModule);
Sync to upstream/release/655 (#1563) ## New Solver * Type functions should be able to signal whether or not irreducibility is due to an error * Do not generate extra expansion constraint for uninvoked user-defined type functions * Print in a user-defined type function reports as an error instead of logging to stdout * Many e-graphs bugfixes and performance improvements * Many general bugfixes and improvements to the new solver as a whole * Fixed issue with used-defined type functions not being able to call each other * Infer types of globals under new type solver ## Fragment Autocomplete * Miscellaneous fixes to make interop with the old solver better ## Runtime * Support disabling specific built-in functions from being fast-called or constant-evaluated (Closes #1538) * New compiler option `disabledBuiltins` accepts a list of library function names like "tonumber" or "math.cos" * Added constant folding for vector arithmetic * Added constant propagation and type inference for vector globals (Fixes #1511) * New compiler option `librariesWithKnownMembers` accepts a list of libraries for members of which a request for constant value and/or type will be made * `libraryMemberTypeCb` callback is called to get the type of a global, return one of the `LuauBytecodeType` values. 'boolean', 'number', 'string' and 'vector' type are supported. * `libraryMemberConstantCb` callback is called to setup the constant value of a global. To set a value, C API `luau_set_compile_constant_*` or C++ API `setCompileConstant*` functions should be used. --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Daniel Angel <danielangel@roblox.com> Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Andrew Miranti <amiranti@roblox.com> Co-authored-by: Shiqi Ai <sai@roblox.com> Co-authored-by: Yohoo Lin <yohoo@roblox.com> Co-authored-by: Daniel Angel <danielangel@roblox.com> Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
2024-12-13 21:02:30 +00:00
LUAU_FASTFLAG(LexerResumesFromPosition2)
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ClassType*> ptr, std::optional<std::string> contents)
{
return std::nullopt;
}
static FrontendOptions getOptions()
{
FrontendOptions options;
options.retainFullTypeGraphs = true;
if (!FFlag::LuauSolverV2)
options.forAutocomplete = true;
options.runLintChecks = false;
return options;
}
template<class BaseType>
struct FragmentAutocompleteFixtureImpl : BaseType
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
{
ScopedFastFlag sffs[5] = {
{FFlag::LuauAllowFragmentParsing, true},
{FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true},
{FFlag::LuauStoreSolverTypeOnModule, true},
{FFlag::LuauSymbolEquality, true},
Sync to upstream/release/655 (#1563) ## New Solver * Type functions should be able to signal whether or not irreducibility is due to an error * Do not generate extra expansion constraint for uninvoked user-defined type functions * Print in a user-defined type function reports as an error instead of logging to stdout * Many e-graphs bugfixes and performance improvements * Many general bugfixes and improvements to the new solver as a whole * Fixed issue with used-defined type functions not being able to call each other * Infer types of globals under new type solver ## Fragment Autocomplete * Miscellaneous fixes to make interop with the old solver better ## Runtime * Support disabling specific built-in functions from being fast-called or constant-evaluated (Closes #1538) * New compiler option `disabledBuiltins` accepts a list of library function names like "tonumber" or "math.cos" * Added constant folding for vector arithmetic * Added constant propagation and type inference for vector globals (Fixes #1511) * New compiler option `librariesWithKnownMembers` accepts a list of libraries for members of which a request for constant value and/or type will be made * `libraryMemberTypeCb` callback is called to get the type of a global, return one of the `LuauBytecodeType` values. 'boolean', 'number', 'string' and 'vector' type are supported. * `libraryMemberConstantCb` callback is called to setup the constant value of a global. To set a value, C API `luau_set_compile_constant_*` or C++ API `setCompileConstant*` functions should be used. --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Daniel Angel <danielangel@roblox.com> Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Andrew Miranti <amiranti@roblox.com> Co-authored-by: Shiqi Ai <sai@roblox.com> Co-authored-by: Yohoo Lin <yohoo@roblox.com> Co-authored-by: Daniel Angel <danielangel@roblox.com> Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
2024-12-13 21:02:30 +00:00
{FFlag::LexerResumesFromPosition2, true}
};
FragmentAutocompleteFixtureImpl()
: BaseType(true)
{
}
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
FragmentAutocompleteAncestryResult runAutocompleteVisitor(const std::string& source, const Position& cursorPos)
{
ParseResult p = this->tryParse(source); // We don't care about parsing incomplete asts
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
REQUIRE(p.root);
return findAncestryForFragmentParse(p.root, cursorPos);
}
FragmentParseResult parseFragment(
const std::string& document,
const Position& cursorPos,
std::optional<Position> fragmentEndPosition = std::nullopt
)
{
SourceModule* srcModule = this->getMainSourceModule();
std::string_view srcString = document;
return Luau::parseFragment(*srcModule, srcString, cursorPos, fragmentEndPosition);
}
2024-10-25 21:15:01 +01:00
CheckResult checkOldSolver(const std::string& source)
2024-10-25 21:15:01 +01:00
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
return this->check(Mode::Strict, source, getOptions());
}
FragmentTypeCheckResult checkFragment(
const std::string& document,
const Position& cursorPos,
std::optional<Position> fragmentEndPosition = std::nullopt
)
{
return Luau::typecheckFragment(this->frontend, "MainModule", cursorPos, getOptions(), document, fragmentEndPosition);
2024-10-25 21:15:01 +01:00
}
FragmentAutocompleteResult autocompleteFragment(
const std::string& document,
Position cursorPos,
std::optional<Position> fragmentEndPosition = std::nullopt
)
{
FrontendOptions options;
return Luau::fragmentAutocomplete(this->frontend, document, "MainModule", cursorPos, getOptions(), nullCallback, fragmentEndPosition);
}
void autocompleteFragmentInBothSolvers(
const std::string& document,
const std::string& updated,
Position cursorPos,
std::function<void(FragmentAutocompleteResult& result)> assertions,
std::optional<Position> fragmentEndPosition = std::nullopt
)
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
this->check(document);
FragmentAutocompleteResult result = autocompleteFragment(updated, cursorPos, fragmentEndPosition);
assertions(result);
ScopedFastFlag _{FFlag::LuauSolverV2, false};
this->check(document, getOptions());
result = autocompleteFragment(updated, cursorPos, fragmentEndPosition);
assertions(result);
}
};
struct FragmentAutocompleteFixture : FragmentAutocompleteFixtureImpl<Fixture>
{
FragmentAutocompleteFixture()
: FragmentAutocompleteFixtureImpl<Fixture>()
{
addGlobalBinding(frontend.globals, "table", Binding{builtinTypes->anyType});
addGlobalBinding(frontend.globals, "math", Binding{builtinTypes->anyType});
addGlobalBinding(frontend.globalsForAutocomplete, "table", Binding{builtinTypes->anyType});
addGlobalBinding(frontend.globalsForAutocomplete, "math", Binding{builtinTypes->anyType});
}
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
};
struct FragmentAutocompleteBuiltinsFixture : FragmentAutocompleteFixtureImpl<BuiltinsFixture>
{
FragmentAutocompleteBuiltinsFixture()
: FragmentAutocompleteFixtureImpl<BuiltinsFixture>()
{
const std::string fakeVecDecl = R"(
declare class FakeVec
function dot(self, x: FakeVec) : FakeVec
zero : FakeVec
end
)";
// The old solver always performs a strict mode check and populates the module resolver and globals
// for autocomplete.
// The new solver just populates the globals and the moduleResolver.
// Because these tests run in both the old solver and the new solver, and the test suite
// now picks the module resolver as appropriate in order to better mimic the studio code path,
// we have to load the definition file into both the 'globals'/'resolver' and the equivalent
// 'for autocomplete'.
loadDefinition(fakeVecDecl);
loadDefinition(fakeVecDecl, /* For Autocomplete Module */ true);
}
};
TEST_SUITE_BEGIN("FragmentAutocompleteTraversalTests");
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "just_two_locals")
{
auto result = runAutocompleteVisitor(
R"(
local x = 4
local y = 5
)",
{2, 11}
);
CHECK_EQ(3, result.ancestry.size());
CHECK_EQ(1, result.localStack.size());
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
CHECK_EQ(result.localMap.size(), result.localStack.size());
REQUIRE(result.nearestStatement);
AstStatLocal* local = result.nearestStatement->as<AstStatLocal>();
REQUIRE(local);
CHECK(1 == local->vars.size);
CHECK_EQ("y", std::string(local->vars.data[0]->name.value));
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "cursor_within_scope_tracks_locals_from_previous_scope")
{
auto result = runAutocompleteVisitor(
R"(
local x = 4
local y = 5
if x == 4 then
local e = y
end
)",
{4, 15}
);
CHECK_EQ(5, result.ancestry.size());
CHECK_EQ(2, result.localStack.size());
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
CHECK_EQ(result.localMap.size(), result.localStack.size());
REQUIRE(result.nearestStatement);
CHECK_EQ("y", std::string(result.localStack.back()->name.value));
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
AstStatLocal* local = result.nearestStatement->as<AstStatLocal>();
REQUIRE(local);
CHECK(1 == local->vars.size);
CHECK_EQ("e", std::string(local->vars.data[0]->name.value));
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "cursor_that_comes_later_shouldnt_capture_locals_in_unavailable_scope")
{
auto result = runAutocompleteVisitor(
R"(
local x = 4
local y = 5
if x == 4 then
local e = y
end
local z = x + x
if y == 5 then
local q = x + y + z
end
)",
{8, 23}
);
CHECK_EQ(6, result.ancestry.size());
CHECK_EQ(3, result.localStack.size());
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
CHECK_EQ(result.localMap.size(), result.localStack.size());
REQUIRE(result.nearestStatement);
CHECK_EQ("z", std::string(result.localStack.back()->name.value));
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
AstStatLocal* local = result.nearestStatement->as<AstStatLocal>();
REQUIRE(local);
CHECK(1 == local->vars.size);
CHECK_EQ("q", std::string(local->vars.data[0]->name.value));
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "nearest_enclosing_statement_can_be_non_local")
{
auto result = runAutocompleteVisitor(
R"(
local x = 4
local y = 5
if x == 4 then
)",
{3, 4}
);
CHECK_EQ(4, result.ancestry.size());
CHECK_EQ(2, result.localStack.size());
CHECK_EQ(result.localMap.size(), result.localStack.size());
REQUIRE(result.nearestStatement);
CHECK_EQ("y", std::string(result.localStack.back()->name.value));
AstStatIf* ifS = result.nearestStatement->as<AstStatIf>();
CHECK(ifS != nullptr);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "local_funcs_show_up_in_local_stack")
{
auto result = runAutocompleteVisitor(
R"(
local function foo() return 4 end
local x = foo()
local function bar() return x + foo() end
)",
{3, 32}
);
CHECK_EQ(8, result.ancestry.size());
CHECK_EQ(2, result.localStack.size());
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
CHECK_EQ(result.localMap.size(), result.localStack.size());
CHECK_EQ("x", std::string(result.localStack.back()->name.value));
Sync to upstream/release/645 (#1440) In this update, we continue to improve the overall stability of the new type solver. We're also shipping some early bits of two new features, one of the language and one of the analysis API: user-defined type functions and an incremental typechecking API. If you use the new solver and want to use all new fixes included in this release, you have to reference an additional Luau flag: ```c++ LUAU_DYNAMIC_FASTINT(LuauTypeSolverRelease) ``` And set its value to `645`: ```c++ DFInt::LuauTypeSolverRelease.value = 645; // Or a higher value for future updates ``` ## New Solver * Fix a crash where scopes are incorrectly accessed cross-module after they've been deallocated by appropriately zeroing out associated scope pointers for free types, generic types, table types, etc. * Fix a crash where we were incorrectly caching results for bound types in generalization. * Eliminated some unnecessary intermediate allocations in the constraint solver and type function infrastructure. * Built some initial groundwork for an incremental typecheck API for use by language servers. * Built an initial technical preview for [user-defined type functions](https://rfcs.luau-lang.org/user-defined-type-functions.html), more work still to come (including calling type functions from other type functions), but adventurous folks wanting to experiment with it can try it out by enabling `FFlag::LuauUserDefinedTypeFunctionsSyntax` and `FFlag::LuauUserDefinedTypeFunction` in their local environment. Special thanks to @joonyoo181 who built up all the initial infrastructure for this during his internship! ## Miscellaneous changes * Fix a compilation error on Ubuntu (fixes #1437) --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Jeremy Yoo <jyoo@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Junseo Yoo <jyoo@roblox.com>
2024-09-27 19:58:21 +01:00
auto returnSt = result.nearestStatement->as<AstStatReturn>();
CHECK(returnSt != nullptr);
}
TEST_SUITE_END();
TEST_SUITE_BEGIN("FragmentAutocompleteParserTests");
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "local_initializer")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
check("local a =");
auto fragment = parseFragment("local a =", Position(0, 10));
CHECK_EQ("local a =", fragment.fragmentToParse);
CHECK_EQ(Location{Position{0, 0}, 9}, fragment.root->location);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "statement_in_empty_fragment_is_non_null")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto res = check(R"(
)");
LUAU_REQUIRE_NO_ERRORS(res);
auto fragment = parseFragment(
R"(
)",
Position(1, 0)
);
CHECK_EQ("\n", fragment.fragmentToParse);
CHECK_EQ(2, fragment.ancestry.size());
REQUIRE(fragment.root);
CHECK_EQ(0, fragment.root->body.size);
auto statBody = fragment.root->as<AstStatBlock>();
CHECK(statBody != nullptr);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_parse_complete_fragments")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto res = check(
R"(
local x = 4
local y = 5
)"
);
LUAU_REQUIRE_NO_ERRORS(res);
auto fragment = parseFragment(
R"(
local x = 4
local y = 5
local z = x + y
)",
Position{3, 15}
);
CHECK_EQ(Location{Position{2, 0}, Position{3, 15}}, fragment.root->location);
CHECK_EQ("local y = 5\nlocal z = x + y", fragment.fragmentToParse);
CHECK_EQ(5, fragment.ancestry.size());
REQUIRE(fragment.root);
CHECK_EQ(2, fragment.root->body.size);
auto stat = fragment.root->body.data[1]->as<AstStatLocal>();
REQUIRE(stat);
CHECK_EQ(1, stat->vars.size);
CHECK_EQ(1, stat->values.size);
CHECK_EQ("z", std::string(stat->vars.data[0]->name.value));
auto bin = stat->values.data[0]->as<AstExprBinary>();
REQUIRE(bin);
CHECK_EQ(AstExprBinary::Op::Add, bin->op);
auto lhs = bin->left->as<AstExprLocal>();
auto rhs = bin->right->as<AstExprLocal>();
REQUIRE(lhs);
REQUIRE(rhs);
CHECK_EQ("x", std::string(lhs->local->name.value));
CHECK_EQ("y", std::string(rhs->local->name.value));
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_parse_fragments_in_line")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto res = check(
R"(
local x = 4
local y = 5
)"
);
LUAU_REQUIRE_NO_ERRORS(res);
auto fragment = parseFragment(
R"(
local x = 4
local z = x + y
local y = 5
)",
Position{2, 15}
);
CHECK_EQ("local z = x + y", fragment.fragmentToParse);
CHECK_EQ(5, fragment.ancestry.size());
REQUIRE(fragment.root);
CHECK_EQ(Location{Position{2, 0}, Position{2, 15}}, fragment.root->location);
CHECK_EQ(1, fragment.root->body.size);
auto stat = fragment.root->body.data[0]->as<AstStatLocal>();
REQUIRE(stat);
CHECK_EQ(1, stat->vars.size);
CHECK_EQ(1, stat->values.size);
CHECK_EQ("z", std::string(stat->vars.data[0]->name.value));
auto bin = stat->values.data[0]->as<AstExprBinary>();
REQUIRE(bin);
CHECK_EQ(AstExprBinary::Op::Add, bin->op);
auto lhs = bin->left->as<AstExprLocal>();
auto rhs = bin->right->as<AstExprGlobal>();
REQUIRE(lhs);
REQUIRE(rhs);
CHECK_EQ("x", std::string(lhs->local->name.value));
CHECK_EQ("y", std::string(rhs->name.value));
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_parse_in_correct_scope")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
check(R"(
local myLocal = 4
function abc()
local myInnerLocal = 1
end
)");
auto fragment = parseFragment(
R"(
local myLocal = 4
function abc()
local myInnerLocal = 1
end
)",
Position{6, 0}
);
CHECK_EQ("\n ", fragment.fragmentToParse);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_parse_single_line_fragment_override")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto res = check("function abc(foo: string) end");
LUAU_REQUIRE_NO_ERRORS(res);
auto callFragment = parseFragment(
R"(function abc(foo: string) end
abc("foo")
abc("bar")
)",
Position{1, 6},
Position{1, 10}
);
CHECK_EQ("function abc(foo: string) end\nabc(\"foo\")", callFragment.fragmentToParse);
CHECK(callFragment.nearestStatement->is<AstStatFunction>());
CHECK_GE(callFragment.ancestry.size(), 2);
AstNode* back = callFragment.ancestry.back();
CHECK(back->is<AstExprConstantString>());
CHECK_EQ(Position{1, 4}, back->location.begin);
CHECK_EQ(Position{1, 9}, back->location.end);
AstNode* parent = callFragment.ancestry.rbegin()[1];
CHECK(parent->is<AstExprCall>());
CHECK_EQ(Position{1, 0}, parent->location.begin);
CHECK_EQ(Position{1, 10}, parent->location.end);
auto stringFragment = parseFragment(
R"(function abc(foo: string) end
abc("foo")
abc("bar")
)",
Position{1, 6},
Position{1, 9}
);
CHECK_EQ("function abc(foo: string) end\nabc(\"foo\")", stringFragment.fragmentToParse);
CHECK(stringFragment.nearestStatement->is<AstStatFunction>());
CHECK_GE(stringFragment.ancestry.size(), 1);
back = stringFragment.ancestry.back();
auto asString = back->as<AstExprConstantString>();
CHECK(asString);
CHECK_EQ(Position{1, 4}, asString->location.begin);
CHECK_EQ(Position{1, 9}, asString->location.end);
CHECK_EQ("foo", std::string{asString->value.data});
CHECK_EQ(AstExprConstantString::QuotedSimple, asString->quoteStyle);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_parse_multi_line_fragment_override")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
auto res = check("function abc(foo: string) end");
LUAU_REQUIRE_NO_ERRORS(res);
auto fragment = parseFragment(
R"(function abc(foo: string) end
abc(
"foo"
)
abc("bar")
)",
Position{2, 5},
Position{3, 1}
);
CHECK_EQ("function abc(foo: string) end\nabc(\n\"foo\"\n)", fragment.fragmentToParse);
CHECK(fragment.nearestStatement->is<AstStatFunction>());
CHECK_GE(fragment.ancestry.size(), 2);
AstNode* back = fragment.ancestry.back();
CHECK(back->is<AstExprConstantString>());
CHECK_EQ(Position{2, 0}, back->location.begin);
CHECK_EQ(Position{2, 5}, back->location.end);
AstNode* parent = fragment.ancestry.rbegin()[1];
CHECK(parent->is<AstExprCall>());
CHECK_EQ(Position{1, 0}, parent->location.begin);
CHECK_EQ(Position{3, 1}, parent->location.end);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "respects_frontend_options")
{
DOES_NOT_PASS_NEW_SOLVER_GUARD();
std::string source = R"(
local tbl = { abc = 1234}
t
)";
fileResolver.source["game/A"] = source;
FrontendOptions opts;
opts.forAutocomplete = true;
frontend.check("game/A", opts);
CHECK_NE(frontend.moduleResolverForAutocomplete.getModule("game/A"), nullptr);
CHECK_EQ(frontend.moduleResolver.getModule("game/A"), nullptr);
FragmentAutocompleteResult result = Luau::fragmentAutocomplete(frontend, source, "game/A", Position{2, 1}, opts, nullCallback);
CHECK_EQ("game/A", result.incrementalModule->name);
CHECK_NE(frontend.moduleResolverForAutocomplete.getModule("game/A"), nullptr);
CHECK_EQ(frontend.moduleResolver.getModule("game/A"), nullptr);
}
TEST_SUITE_END();
2024-10-25 21:15:01 +01:00
TEST_SUITE_BEGIN("FragmentAutocompleteTypeCheckerTests");
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_typecheck_simple_fragment")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
2024-10-25 21:15:01 +01:00
auto res = check(
R"(
local x = 4
local y = 5
)"
);
LUAU_REQUIRE_NO_ERRORS(res);
auto fragment = checkFragment(
R"(
local x = 4
local y = 5
local z = x + y
)",
Position{3, 15}
);
auto opt = linearSearchForBinding(fragment.freshScope.get(), "z");
2024-10-25 21:15:01 +01:00
REQUIRE(opt);
CHECK_EQ("number", toString(*opt));
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_typecheck_fragment_inserted_inline")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, true};
2024-10-25 21:15:01 +01:00
auto res = check(
R"(
local x = 4
local y = 5
)"
);
LUAU_REQUIRE_NO_ERRORS(res);
auto fragment = checkFragment(
R"(
local x = 4
local z = x
local y = 5
)",
Position{2, 11}
);
auto correct = linearSearchForBinding(fragment.freshScope.get(), "z");
2024-10-25 21:15:01 +01:00
REQUIRE(correct);
CHECK_EQ("number", toString(*correct));
}
TEST_SUITE_END();
TEST_SUITE_BEGIN("MixedModeTests");
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "mixed_mode_basic_example_append")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
auto res = checkOldSolver(
R"(
local x = 4
local y = 5
)"
);
LUAU_REQUIRE_NO_ERRORS(res);
auto fragment = checkFragment(
R"(
local x = 4
local y = 5
local z = x + y
)",
Position{3, 15}
);
auto opt = linearSearchForBinding(fragment.freshScope.get(), "z");
REQUIRE(opt);
CHECK_EQ("number", toString(*opt));
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "mixed_mode_basic_example_inlined")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
auto res = checkOldSolver(
R"(
local x = 4
local y = 5
)"
);
auto fragment = checkFragment(
R"(
local x = 4
local z = x
local y = 5
)",
Position{2, 11}
);
auto correct = linearSearchForBinding(fragment.freshScope.get(), "z");
REQUIRE(correct);
CHECK_EQ("number", toString(*correct));
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "mixed_mode_can_autocomplete_simple_property_access")
{
ScopedFastFlag sff{FFlag::LuauSolverV2, false};
auto res = checkOldSolver(
R"(
local tbl = { abc = 1234}
)"
);
LUAU_REQUIRE_NO_ERRORS(res);
auto fragment = autocompleteFragment(
R"(
local tbl = { abc = 1234}
tbl.
)",
Position{2, 5}
);
LUAU_ASSERT(fragment.freshScope);
CHECK_EQ(1, fragment.acResults.entryMap.size());
CHECK(fragment.acResults.entryMap.count("abc"));
CHECK_EQ(AutocompleteContext::Property, fragment.acResults.context);
}
TEST_SUITE_END();
TEST_SUITE_BEGIN("FragmentAutocompleteTests");
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_autocomplete_simple_property_access")
{
const std::string source = R"(
local tbl = { abc = 1234}
)";
const std::string updated = R"(
local tbl = { abc = 1234}
tbl.
)";
autocompleteFragmentInBothSolvers(
source,
updated,
Position{2, 5},
[](FragmentAutocompleteResult& fragment)
{
LUAU_ASSERT(fragment.freshScope);
CHECK_EQ(1, fragment.acResults.entryMap.size());
CHECK(fragment.acResults.entryMap.count("abc"));
CHECK_EQ(AutocompleteContext::Property, fragment.acResults.context);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "can_autocomplete_nested_property_access")
{
const std::string source = R"(
local tbl = { abc = { def = 1234, egh = false } }
)";
const std::string updated = R"(
local tbl = { abc = { def = 1234, egh = false } }
tbl.abc.
)";
autocompleteFragmentInBothSolvers(
source,
updated,
Position{2, 8},
[](FragmentAutocompleteResult& fragment)
{
LUAU_ASSERT(fragment.freshScope);
CHECK_EQ(2, fragment.acResults.entryMap.size());
CHECK(fragment.acResults.entryMap.count("def"));
CHECK(fragment.acResults.entryMap.count("egh"));
CHECK_EQ(fragment.acResults.context, AutocompleteContext::Property);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "inline_autocomplete_picks_the_right_scope")
{
const std::string source = R"(
type Table = { a: number, b: number }
do
type Table = { x: string, y: string }
end
)";
const std::string updated = R"(
type Table = { a: number, b: number }
do
type Table = { x: string, y: string }
local a : T
end
)";
autocompleteFragmentInBothSolvers(
source,
updated,
Position{4, 15},
[](FragmentAutocompleteResult& fragment)
{
LUAU_ASSERT(fragment.freshScope);
REQUIRE(fragment.acResults.entryMap.count("Table"));
REQUIRE(fragment.acResults.entryMap["Table"].type);
const TableType* tv = get<TableType>(follow(*fragment.acResults.entryMap["Table"].type));
REQUIRE(tv);
CHECK(tv->props.count("x"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "nested_recursive_function")
{
const std::string source = R"(
function foo()
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{2, 0},
[](FragmentAutocompleteResult& fragment)
{
CHECK(fragment.acResults.entryMap.count("foo"));
CHECK_EQ(AutocompleteContext::Statement, fragment.acResults.context);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "string_literal_with_override")
{
const std::string source = R"(
function foo(bar: string) end
foo("abc")
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{2, 6},
[](FragmentAutocompleteResult& fragment)
{
CHECK(fragment.acResults.entryMap.empty());
CHECK_EQ(AutocompleteContext::String, fragment.acResults.context);
},
Position{2, 9}
);
}
// Start compatibility tests!
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "empty_program")
{
autocompleteFragmentInBothSolvers(
"",
"",
Position{0, 1},
[](FragmentAutocompleteResult& frag)
{
auto ac = frag.acResults;
CHECK(ac.entryMap.count("table"));
CHECK(ac.entryMap.count("math"));
CHECK_EQ(ac.context, AutocompleteContext::Statement);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "local_initializer")
{
const std::string source = "local a =";
autocompleteFragmentInBothSolvers(
source,
source,
Position{0, 9},
[](FragmentAutocompleteResult& frag)
{
auto ac = frag.acResults;
CHECK(ac.entryMap.count("table"));
CHECK(ac.entryMap.count("math"));
CHECK_EQ(ac.context, AutocompleteContext::Expression);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "leave_numbers_alone")
{
const std::string source = "local a = 3.";
autocompleteFragmentInBothSolvers(
source,
source,
Position{0, 12},
[](FragmentAutocompleteResult& frag)
{
auto ac = frag.acResults;
CHECK(ac.entryMap.empty());
CHECK_EQ(ac.context, AutocompleteContext::Unknown);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "user_defined_globals")
{
const std::string source = "local myLocal = 4; ";
autocompleteFragmentInBothSolvers(
source,
source,
Position{0, 18},
[](FragmentAutocompleteResult& frag)
{
auto ac = frag.acResults;
CHECK(ac.entryMap.count("myLocal"));
CHECK(ac.entryMap.count("table"));
CHECK(ac.entryMap.count("math"));
CHECK_EQ(ac.context, AutocompleteContext::Statement);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "dont_suggest_local_before_its_definition")
{
const std::string source = R"(
local myLocal = 4
function abc()
local myInnerLocal = 1
end
)";
// autocomplete after abc but before myInnerLocal
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 0},
[](FragmentAutocompleteResult& fragment)
{
auto ac = fragment.acResults;
CHECK(ac.entryMap.count("myLocal"));
LUAU_CHECK_HAS_NO_KEY(ac.entryMap, "myInnerLocal");
}
);
// autocomplete after my inner local
autocompleteFragmentInBothSolvers(
source,
source,
Position{4, 0},
[](FragmentAutocompleteResult& fragment)
{
auto ac = fragment.acResults;
CHECK(ac.entryMap.count("myLocal"));
CHECK(ac.entryMap.count("myInnerLocal"));
}
);
// autocomplete after abc, but don't include myInnerLocal(in the hidden scope)
autocompleteFragmentInBothSolvers(
source,
source,
Position{6, 0},
[](FragmentAutocompleteResult& fragment)
{
auto ac = fragment.acResults;
CHECK(ac.entryMap.count("myLocal"));
LUAU_CHECK_HAS_NO_KEY(ac.entryMap, "myInnerLocal");
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "nested_recursive_function")
{
const std::string source = R"(
local function outer()
local function inner()
end
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 0},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK(ac.entryMap.count("inner"));
CHECK(ac.entryMap.count("outer"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "user_defined_local_functions_in_own_definition")
{
const std::string source = R"(
local function abc()
end
)";
// Autocomplete inside of abc
autocompleteFragmentInBothSolvers(
source,
source,
Position{2, 0},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK(ac.entryMap.count("abc"));
CHECK(ac.entryMap.count("table"));
CHECK(ac.entryMap.count("math"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "global_functions_are_not_scoped_lexically")
{
const std::string source = R"(
if true then
function abc()
end
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{6, 0},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK(!ac.entryMap.empty());
CHECK(ac.entryMap.count("abc"));
CHECK(ac.entryMap.count("table"));
CHECK(ac.entryMap.count("math"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "local_functions_fall_out_of_scope")
{
const std::string source = R"(
if true then
local function abc()
end
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{6, 0},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK_NE(0, ac.entryMap.size());
LUAU_CHECK_HAS_NO_KEY(ac.entryMap, "abc");
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "function_parameters")
{
const std::string source = R"(
function abc(test)
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 0},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK(ac.entryMap.count("test"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "unsealed_table")
{
const std::string source = R"(
local tbl = {}
tbl.prop = 5
tbl.
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 12},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK_EQ(1, ac.entryMap.size());
CHECK(ac.entryMap.count("prop"));
CHECK_EQ(ac.context, AutocompleteContext::Property);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "unsealed_table_2")
{
const std::string source = R"(
local tbl = {}
local inner = { prop = 5 }
tbl.inner = inner
tbl.inner.
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{4, 18},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK_EQ(1, ac.entryMap.size());
CHECK(ac.entryMap.count("prop"));
CHECK_EQ(ac.context, AutocompleteContext::Property);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "cyclic_table")
{
const std::string source = R"(
local abc = {}
local def = { abc = abc }
abc.def = def
abc.def.
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{4, 16},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK(ac.entryMap.count("abc"));
CHECK_EQ(ac.context, AutocompleteContext::Property);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "table_union")
{
const std::string source = R"(
type t1 = { a1 : string, b2 : number }
type t2 = { b2 : string, c3 : string }
function func(abc : t1 | t2)
end
)";
const std::string updated = R"(
type t1 = { a1 : string, b2 : number }
type t2 = { b2 : string, c3 : string }
function func(abc : t1 | t2)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
updated,
Position{4, 16},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK_EQ(1, ac.entryMap.size());
CHECK(ac.entryMap.count("b2"));
CHECK_EQ(ac.context, AutocompleteContext::Property);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "table_intersection")
{
const std::string source = R"(
type t1 = { a1 : string, b2 : number }
type t2 = { b2 : number, c3 : string }
function func(abc : t1 & t2)
end
)";
const std::string updated = R"(
type t1 = { a1 : string, b2 : number }
type t2 = { b2 : number, c3 : string }
function func(abc : t1 & t2)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
updated,
Position{4, 16},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK_EQ(3, ac.entryMap.size());
CHECK(ac.entryMap.count("a1"));
CHECK(ac.entryMap.count("b2"));
CHECK(ac.entryMap.count("c3"));
CHECK_EQ(ac.context, AutocompleteContext::Property);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "get_suggestions_for_the_very_start_of_the_script")
{
const std::string source = R"(
function aaa() end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{0, 0},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK(ac.entryMap.count("table"));
CHECK_EQ(ac.context, AutocompleteContext::Statement);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "studio_ice_1")
{
const std::string source = R"(
--Woop
@native
local function test()
end
)";
const std::string updated = R"(
--Woop
@native
local function test()
end
function a
)";
autocompleteFragmentInBothSolvers(source, updated, Position{6, 10}, [](FragmentAutocompleteResult& result) {});
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "method_call_inside_function_body")
{
const std::string source = R"(
local game = { GetService=function(s) return 'hello' end }
function a()
end
)";
const std::string updated = R"(
local game = { GetService=function(s) return 'hello' end }
function a()
game:
end
)";
autocompleteFragmentInBothSolvers(
source,
updated,
Position{4, 17},
[](FragmentAutocompleteResult& result)
{
auto ac = result.acResults;
CHECK_NE(0, ac.entryMap.size());
LUAU_CHECK_HAS_NO_KEY(ac.entryMap, "math");
CHECK_EQ(ac.context, AutocompleteContext::Property);
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tbl_function_parameter")
{
const std::string source = R"(
--!strict
type Foo = {x : number, y : number}
local function func(abc : Foo)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{4, 7},
[](FragmentAutocompleteResult& result)
{
CHECK_EQ(2, result.acResults.entryMap.size());
CHECK(result.acResults.entryMap.count("x"));
CHECK(result.acResults.entryMap.count("y"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tbl_local_function_parameter")
{
const std::string source = R"(
--!strict
type Foo = {x : number, y : number}
local function func(abc : Foo)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{4, 7},
[](FragmentAutocompleteResult& result)
{
CHECK_EQ(2, result.acResults.entryMap.size());
CHECK(result.acResults.entryMap.count("x"));
CHECK(result.acResults.entryMap.count("y"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "vec3_function_parameter")
{
const std::string source = R"(
--!strict
local function func(abc : FakeVec)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 7},
[](FragmentAutocompleteResult& result)
{
CHECK_EQ(2, result.acResults.entryMap.size());
CHECK(result.acResults.entryMap.count("zero"));
CHECK(result.acResults.entryMap.count("dot"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "vec3_local_function_parameter")
{
const std::string source = R"(
--!strict
local function func(abc : FakeVec)
abc.
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{3, 7},
[](FragmentAutocompleteResult& result)
{
CHECK_EQ(2, result.acResults.entryMap.size());
CHECK(result.acResults.entryMap.count("zero"));
CHECK(result.acResults.entryMap.count("dot"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "function_parameter_not_recommending_out_of_scope_argument")
{
const std::string source = R"(
--!strict
local function foo(abd: FakeVec)
end
local function bar(abc : FakeVec)
a
end
)";
autocompleteFragmentInBothSolvers(
source,
source,
Position{5, 5},
[](FragmentAutocompleteResult& result)
{
CHECK(result.acResults.entryMap.count("abc"));
CHECK(!result.acResults.entryMap.count("abd"));
}
);
}
TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "bad_range")
{
const std::string source = R"(
l
)";
const std::string updated = R"(
local t = 1
t
)";
autocompleteFragmentInBothSolvers(
source,
updated,
Position{2, 1},
[](FragmentAutocompleteResult& result)
{
auto opt = linearSearchForBinding(result.freshScope, "t");
REQUIRE(opt);
CHECK_EQ("number", toString(*opt));
}
);
}
TEST_SUITE_END();