Skip to content

Commit b6811a8

Browse files
committed
Support disabling globals from global scope
Setting that accepts a list of libraries (`table`) or library methods (`string.split`) Closes #888
1 parent 3b4af31 commit b6811a8

File tree

6 files changed

+148
-4
lines changed

6 files changed

+148
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Added configuration `luau-lsp.types.disabledGlobals` to support removing globals from the main scope for analysis.
12+
Accepts a list of libraries or library methods (e.g., `table`, `string.split`,
13+
etc.) ([#888](https://github.com/JohnnyMorganz/luau-lsp/issues/888))
14+
915
### Changed
1016

1117
- Sync to upstream Luau 0.663

editors/code/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,15 @@
294294
},
295295
"scope": "window"
296296
},
297+
"luau-lsp.types.disabledGlobals": {
298+
"markdownDescription": "A list of globals to remove from the global scope. Accepts full libraries or particular functions (e.g., `table` or `table.clone`)",
299+
"type": "array",
300+
"default": [],
301+
"items": {
302+
"type": "string"
303+
},
304+
"scope": "window"
305+
},
297306
"luau-lsp.types.roblox": {
298307
"markdownDescription": "Load in and automatically update Roblox type definitions for the type checker",
299308
"type": "boolean",

src/Workspace.cpp

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,72 @@ void WorkspaceFolder::indexFiles(const ClientConfiguration& config)
300300
client->sendTrace("workspace: indexing all files COMPLETED");
301301
}
302302

303-
void WorkspaceFolder::registerTypes()
303+
static void clearDisabledGlobals(const ClientPtr client, const Luau::GlobalTypes& globalTypes, const std::vector<std::string>& disabledGlobals)
304+
{
305+
const auto targetScope = globalTypes.globalScope;
306+
for (const auto& disabledGlobal : disabledGlobals)
307+
{
308+
std::string library = disabledGlobal;
309+
std::optional<std::string> method = std::nullopt;
310+
311+
if (const auto separator = disabledGlobal.find('.'); separator != std::string::npos)
312+
{
313+
library = disabledGlobal.substr(0, separator);
314+
method = disabledGlobal.substr(separator + 1);
315+
}
316+
317+
const auto globalName = globalTypes.globalNames.names->get(library.c_str());
318+
if (globalName.value == nullptr)
319+
{
320+
client->sendLogMessage(lsp::MessageType::Warning, "disabling globals: skipping unknown global - " + disabledGlobal);
321+
continue;
322+
}
323+
324+
if (auto binding = targetScope->bindings.find(globalName); binding != targetScope->bindings.end())
325+
{
326+
if (method)
327+
{
328+
const auto typeId = Luau::follow(binding->second.typeId);
329+
if (const auto ttv = Luau::getMutable<Luau::TableType>(typeId))
330+
{
331+
if (contains(ttv->props, *method))
332+
{
333+
client->sendLogMessage(lsp::MessageType::Info, "disabling globals: erasing global - " + disabledGlobal);
334+
ttv->props.erase(*method);
335+
}
336+
else
337+
client->sendLogMessage(lsp::MessageType::Warning, "disabling globals: could not find method - " + disabledGlobal);
338+
}
339+
else if (const auto ctv = Luau::getMutable<Luau::ClassType>(typeId))
340+
{
341+
if (contains(ctv->props, *method))
342+
{
343+
client->sendLogMessage(lsp::MessageType::Info, "disabling globals: erasing global - " + disabledGlobal);
344+
ttv->props.erase(*method);
345+
}
346+
else
347+
client->sendLogMessage(lsp::MessageType::Warning, "disabling globals: could not find method - " + disabledGlobal);
348+
}
349+
else
350+
{
351+
client->sendLogMessage(lsp::MessageType::Warning,
352+
"disabling globals: cannot clear method from global, only tables or classes are supported - " + disabledGlobal);
353+
}
354+
}
355+
else
356+
{
357+
client->sendLogMessage(lsp::MessageType::Info, "disabling globals: erasing global - " + disabledGlobal);
358+
targetScope->bindings.erase(globalName);
359+
}
360+
}
361+
else
362+
{
363+
client->sendLogMessage(lsp::MessageType::Warning, "disabling globals: skipping unknown global - " + disabledGlobal);
364+
}
365+
}
366+
}
367+
368+
void WorkspaceFolder::registerTypes(const std::vector<std::string>& disabledGlobals)
304369
{
305370
LUAU_TIMETRACE_SCOPE("WorkspaceFolder::initialize", "LSP");
306371
client->sendTrace("workspace initialization: registering Luau globals");
@@ -368,6 +433,16 @@ void WorkspaceFolder::registerTypes()
368433
client->publishDiagnostics({uri, std::nullopt, diagnostics});
369434
}
370435
}
436+
437+
if (!disabledGlobals.empty())
438+
{
439+
client->sendTrace("workspace initialization: removing disabled globals");
440+
clearDisabledGlobals(client, frontend.globals, disabledGlobals);
441+
if (!FFlag::LuauSolverV2)
442+
clearDisabledGlobals(client, frontend.globalsForAutocomplete, disabledGlobals);
443+
client->sendTrace("workspace initialization: removing disabled globals COMPLETED");
444+
}
445+
371446
Luau::freeze(frontend.globals.globalTypes);
372447
if (!FFlag::LuauSolverV2)
373448
Luau::freeze(frontend.globalsForAutocomplete.globalTypes);
@@ -387,7 +462,7 @@ void WorkspaceFolder::setupWithConfiguration(const ClientConfiguration& configur
387462
platform = LSPPlatform::getPlatform(configuration, &fileResolver, this);
388463
fileResolver.platform = platform.get();
389464

390-
registerTypes();
465+
registerTypes(configuration.types.disabledGlobals);
391466
}
392467

393468
client->sendTrace("workspace: apply platform-specific configuration");

src/include/LSP/ClientConfiguration.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ struct ClientTypesConfiguration
3636
bool roblox = true;
3737
/// Any definition files to load globally
3838
std::vector<std::filesystem::path> definitionFiles{};
39+
/// A list of globals to remove from the global scope. Accepts full libraries or particular functions (e.g., `table` or `table.clone`)
40+
std::vector<std::string> disabledGlobals{};
3941
};
40-
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ClientTypesConfiguration, roblox, definitionFiles);
42+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ClientTypesConfiguration, roblox, definitionFiles, disabledGlobals);
4143

4244
enum struct InlayHintsParameterNamesConfig
4345
{

src/include/LSP/Workspace.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class WorkspaceFolder
8484
const Luau::ModulePtr getModule(const Luau::ModuleName& moduleName, bool forAutocomplete = false) const;
8585

8686
private:
87-
void registerTypes();
87+
void registerTypes(const std::vector<std::string>& disabledGlobals);
8888
void endAutocompletion(const lsp::CompletionParams& params);
8989
void suggestImports(const Luau::ModuleName& moduleName, const Luau::Position& position, const ClientConfiguration& config,
9090
const TextDocument& textDocument, std::vector<lsp::CompletionItem>& result, bool completingTypeReferencePrefix = true);

tests/Definitions.test.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,56 @@ TEST_CASE("handles_definitions_files_relying_on_mutations")
4242
REQUIRE(result.errors.empty());
4343
}
4444

45+
TEST_CASE("support_disabling_global_types")
46+
{
47+
auto client = std::make_shared<Client>(Client{});
48+
auto workspace = WorkspaceFolder(client, "$TEST_WORKSPACE", Uri(), std::nullopt);
49+
50+
auto config = defaultTestClientConfiguration();
51+
config.types.disabledGlobals = {
52+
"table",
53+
};
54+
55+
workspace.setupWithConfiguration(config);
56+
57+
auto document = newDocument(workspace, "foo.luau", R"(
58+
local x = string.split("", "")
59+
local y = table.insert({}, 1)
60+
)");
61+
62+
auto result = workspace.frontend.check("foo.luau");
63+
REQUIRE_EQ(result.errors.size(), 1);
64+
65+
auto err = Luau::get<Luau::UnknownSymbol>(result.errors[0]);
66+
REQUIRE(err);
67+
CHECK_EQ(err->name, "table");
68+
CHECK_EQ(err->context, Luau::UnknownSymbol::Context::Binding);
69+
}
70+
71+
TEST_CASE("support_disabling_methods_in_global_types")
72+
{
73+
auto client = std::make_shared<Client>(Client{});
74+
auto workspace = WorkspaceFolder(client, "$TEST_WORKSPACE", Uri(), std::nullopt);
75+
76+
auto config = defaultTestClientConfiguration();
77+
config.types.disabledGlobals = {
78+
"table.insert",
79+
};
80+
81+
workspace.setupWithConfiguration(config);
82+
83+
auto document = newDocument(workspace, "foo.luau", R"(
84+
local x = table.find({}, "value")
85+
local y = table.insert({}, 1)
86+
)");
87+
88+
auto result = workspace.frontend.check("foo.luau");
89+
REQUIRE_EQ(result.errors.size(), 1);
90+
91+
auto err = Luau::get<Luau::UnknownProperty>(result.errors[0]);
92+
REQUIRE(err);
93+
CHECK_EQ(Luau::toString(err->table), "typeof(table)");
94+
CHECK_EQ(err->key, "insert");
95+
}
96+
4597
TEST_SUITE_END();

0 commit comments

Comments
 (0)