From 36f82fff3c5acdebaf1897882a303271c4bf6728 Mon Sep 17 00:00:00 2001 From: Janne Hellsten Date: Fri, 5 Jan 2024 19:51:44 +0200 Subject: [PATCH] luau-analyze: add --definitions= option for loading type definition files Add the ability to load a type definition file before type checking. This is useful in projects that'd like to declare their own types (such as types declared by their game engine's Luau integration code) and linting their scripts using 'luau-analyze'. This is modeled to behave similar to the luau-lsp.types.definitionFiles in the Luau LSP project (https://github.com/JohnnyMorganz/luau-lsp). Usage example: luau-analyze --definitions=engine_types.d.luau some_script.luau Note: doesn't introduce new clang-format delta, but the edited file already deviates from what clang-format produces. for #1140 --- CLI/Analyze.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/CLI/Analyze.cpp b/CLI/Analyze.cpp index abe28b11..3a5fbcc6 100644 --- a/CLI/Analyze.cpp +++ b/CLI/Analyze.cpp @@ -20,6 +20,7 @@ #include #endif + LUAU_FASTFLAG(DebugLuauTimeTracing) enum class ReportFormat @@ -124,6 +125,7 @@ static void displayHelp(const char* argv0) printf(" --formatter=gnu: report analysis errors in GNU-compatible format\n"); printf(" --mode=strict: default to strict mode when typechecking\n"); printf(" --timetrace: record compiler time tracing information into trace.json\n"); + printf(" --definitions=: load type definition file (specify multiple times for many files)\n"); } static int assertionHandler(const char* expr, const char* file, int line, const char* function) @@ -290,6 +292,57 @@ private: std::queue> tasks; }; +struct DefinitionsResolver +{ + bool success = true; + std::vector> loadErrors; + std::vector> parseErrors; + std::vector> typeErrors; + + static DefinitionsResolver loadAndRegister(Luau::Frontend& frontend, const std::vector& definitionsFiles) + { + DefinitionsResolver loadResult; + + for (const auto& defFile : definitionsFiles) + { + auto contents = readFile(defFile); + if (contents == std::nullopt) + { + loadResult.success = false; + loadResult.loadErrors.push_back({defFile, "definition file not found"}); + } + else + { + auto res = frontend.loadDefinitionFile(frontend.globals, frontend.globals.globalScope, contents.value(), "@user", + /* captureComments = */ false, /*typeCheckForAutocomplete*/ false); + if (!res.success) + { + loadResult.success = false; + for (auto& error : res.parseResult.errors) + loadResult.parseErrors.push_back({defFile, error}); + if (res.module) + { + for (auto& error : res.module->errors) + loadResult.typeErrors.push_back({defFile, error}); + } + } + } + } + return loadResult; + } + + bool reportErrors(ReportFormat format) + { + for (const auto& err : loadErrors) + report(format, err.first.c_str(), Luau::Location(), "CLI", err.second.c_str()); + for (const auto& err : parseErrors) + report(format, err.first.c_str(), err.second.getLocation(), "SyntaxError", err.second.getMessage().c_str()); + for (const auto& err : typeErrors) + report(format, err.first.c_str(), err.second.location, "TypeError", Luau::toString(err.second).c_str()); + return success; + } +}; + int main(int argc, char** argv) { Luau::assertHandler() = assertionHandler; @@ -307,6 +360,8 @@ int main(int argc, char** argv) bool annotate = false; int threadCount = 0; + std::vector definitionsFiles; + for (int i = 1; i < argc; ++i) { if (argv[i][0] != '-') @@ -326,6 +381,10 @@ int main(int argc, char** argv) setLuauFlags(argv[i] + 9); else if (strncmp(argv[i], "-j", 2) == 0) threadCount = int(strtol(argv[i] + 2, nullptr, 10)); + else if (strncmp(argv[i], "--definitions=", 11) == 0) + { + definitionsFiles.push_back(argv[i] + 14); + } } #if !defined(LUAU_ENABLE_TIME_TRACE) @@ -345,6 +404,7 @@ int main(int argc, char** argv) Luau::Frontend frontend(&fileResolver, &configResolver, frontendOptions); Luau::registerBuiltinGlobals(frontend, frontend.globals); + DefinitionsResolver defFilesRes = DefinitionsResolver::loadAndRegister(frontend, definitionsFiles); Luau::freeze(frontend.globals.globalTypes); #ifdef CALLGRIND @@ -387,6 +447,8 @@ int main(int argc, char** argv) int failed = 0; + failed += !defFilesRes.reportErrors(format); + for (const Luau::ModuleName& name : checkedModules) failed += !reportModuleResult(frontend, name, format, annotate);