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);