// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once #include "Luau/Config.h" #include "Luau/GlobalTypes.h" #include "Luau/Module.h" #include "Luau/ModuleResolver.h" #include "Luau/RequireTracer.h" #include "Luau/Scope.h" #include "Luau/TypeCheckLimits.h" #include "Luau/Variant.h" #include "Luau/AnyTypeSummary.h" #include #include #include #include namespace Luau { class AstStat; class ParseError; struct Frontend; struct TypeError; struct LintWarning; struct GlobalTypes; struct TypeChecker; struct FileResolver; struct ModuleResolver; struct ParseResult; struct HotComment; struct BuildQueueItem; struct FrontendCancellationToken; struct AnyTypeSummary; struct LoadDefinitionFileResult { bool success; ParseResult parseResult; SourceModule sourceModule; ModulePtr module; }; std::optional parseMode(const std::vector& hotcomments); struct SourceNode { bool hasDirtySourceModule() const { return dirtySourceModule; } bool hasDirtyModule(bool forAutocomplete) const { return forAutocomplete ? dirtyModuleForAutocomplete : dirtyModule; } ModuleName name; std::string humanReadableName; DenseHashSet requireSet{{}}; std::vector> requireLocations; bool dirtySourceModule = true; bool dirtyModule = true; bool dirtyModuleForAutocomplete = true; double autocompleteLimitsMult = 1.0; }; struct FrontendOptions { // When true, we retain full type information about every term in the AST. // Setting this to false cuts back on RAM and is a good idea for batch // jobs where the type graph is not deeply inspected after typechecking // is complete. bool retainFullTypeGraphs = false; // Run typechecking only in mode required for autocomplete (strict mode in // order to get more precise type information) bool forAutocomplete = false; bool runLintChecks = false; // If not empty, randomly shuffle the constraint set before attempting to // solve. Use this value to seed the random number generator. std::optional randomizeConstraintResolutionSeed; std::optional enabledLintWarnings; std::shared_ptr cancellationToken; // Time limit for typechecking a single module std::optional moduleTimeLimitSec; // When true, some internal complexity limits will be scaled down for modules that miss the limit set by moduleTimeLimitSec bool applyInternalLimitScaling = false; // An optional callback which is called for every *dirty* module was checked // Is multi-threaded typechecking is used, this callback might be called from multiple threads and has to be thread-safe std::function customModuleCheck; }; struct CheckResult { std::vector errors; LintResult lintResult; std::vector timeoutHits; }; struct FrontendModuleResolver : ModuleResolver { FrontendModuleResolver(Frontend* frontend); const ModulePtr getModule(const ModuleName& moduleName) const override; bool moduleExists(const ModuleName& moduleName) const override; std::optional resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override; std::string getHumanReadableModuleName(const ModuleName& moduleName) const override; void setModule(const ModuleName& moduleName, ModulePtr module); void clearModules(); private: Frontend* frontend; mutable std::mutex moduleMutex; std::unordered_map modules; }; struct Frontend { struct Stats { size_t files = 0; size_t lines = 0; size_t filesStrict = 0; size_t filesNonstrict = 0; double timeRead = 0; double timeParse = 0; double timeCheck = 0; double timeLint = 0; }; Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options = {}); // Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking void parse(const ModuleName& name); // Parse and typecheck module graph CheckResult check(const ModuleName& name, std::optional optionOverride = {}); // new shininess bool isDirty(const ModuleName& name, bool forAutocomplete = false) const; void markDirty(const ModuleName& name, std::vector* markedDirty = nullptr); /** Borrow a pointer into the SourceModule cache. * * Returns nullptr if we don't have it. This could mean that the script * doesn't exist, or simply that its contents have changed since the previous * check, in which case we do not have its AST. * * IMPORTANT: this pointer is only valid until the next call to markDirty. Do not retain it. */ SourceModule* getSourceModule(const ModuleName& name); const SourceModule* getSourceModule(const ModuleName& name) const; void clearStats(); void clear(); ScopePtr addEnvironment(const std::string& environmentName); ScopePtr getEnvironmentScope(const std::string& environmentName) const; void registerBuiltinDefinition(const std::string& name, std::function); void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName); LoadDefinitionFileResult loadDefinitionFile( GlobalTypes& globals, ScopePtr targetScope, std::string_view source, const std::string& packageName, bool captureComments, bool typeCheckForAutocomplete = false ); // Batch module checking. Queue modules and check them together, retrieve results with 'getCheckResult' // If provided, 'executeTask' function is allowed to call the 'task' function on any thread and return without waiting for 'task' to complete void queueModuleCheck(const std::vector& names); void queueModuleCheck(const ModuleName& name); std::vector checkQueuedModules( std::optional optionOverride = {}, std::function task)> executeTask = {}, std::function progress = {} ); std::optional getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false); std::vector getRequiredScripts(const ModuleName& name); private: ModulePtr check( const SourceModule& sourceModule, Mode mode, std::vector requireCycles, std::optional environmentScope, bool forAutocomplete, bool recordJsonLog, TypeCheckLimits typeCheckLimits ); std::pair getSourceNode(const ModuleName& name); SourceModule parse(const ModuleName& name, std::string_view src, const ParseOptions& parseOptions); bool parseGraph( std::vector& buildQueue, const ModuleName& root, bool forAutocomplete, std::function canSkip = {} ); void addBuildQueueItems( std::vector& items, std::vector& buildQueue, bool cycleDetected, DenseHashSet& seen, const FrontendOptions& frontendOptions ); void checkBuildQueueItem(BuildQueueItem& item); void checkBuildQueueItems(std::vector& items); void recordItemResult(const BuildQueueItem& item); static LintResult classifyLints(const std::vector& warnings, const Config& config); ScopePtr getModuleEnvironment(const SourceModule& module, const Config& config, bool forAutocomplete) const; std::unordered_map environments; std::unordered_map> builtinDefinitions; BuiltinTypes builtinTypes_; public: const NotNull builtinTypes; FileResolver* fileResolver; FrontendModuleResolver moduleResolver; FrontendModuleResolver moduleResolverForAutocomplete; GlobalTypes globals; GlobalTypes globalsForAutocomplete; ConfigResolver* configResolver; FrontendOptions options; InternalErrorReporter iceHandler; std::function prepareModuleScope; std::function writeJsonLog = {}; std::unordered_map> sourceNodes; std::unordered_map> sourceModules; std::unordered_map requireTrace; Stats stats = {}; std::vector moduleQueue; }; ModulePtr check( const SourceModule& sourceModule, Mode mode, const std::vector& requireCycles, NotNull builtinTypes, NotNull iceHandler, NotNull moduleResolver, NotNull fileResolver, const ScopePtr& globalScope, std::function prepareModuleScope, FrontendOptions options, TypeCheckLimits limits ); ModulePtr check( const SourceModule& sourceModule, Mode mode, const std::vector& requireCycles, NotNull builtinTypes, NotNull iceHandler, NotNull moduleResolver, NotNull fileResolver, const ScopePtr& globalScope, std::function prepareModuleScope, FrontendOptions options, TypeCheckLimits limits, bool recordJsonLog, std::function writeJsonLog ); } // namespace Luau