luau/Analysis/include/Luau/Frontend.h

311 lines
11 KiB
C
Raw Normal View History

// 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"
2023-09-08 07:24:03 +08:00
#include "Luau/GlobalTypes.h"
#include "Luau/Module.h"
#include "Luau/ModuleResolver.h"
#include "Luau/RequireTracer.h"
2022-07-01 07:29:02 +08:00
#include "Luau/Scope.h"
2023-07-28 19:37:00 +08:00
#include "Luau/TypeCheckLimits.h"
#include "Luau/Variant.h"
2024-07-26 08:10:42 +08:00
#include "Luau/AnyTypeSummary.h"
2023-04-22 05:41:03 +08:00
#include <mutex>
#include <string>
#include <vector>
#include <optional>
namespace Luau
{
class AstStat;
class ParseError;
struct Frontend;
struct TypeError;
struct LintWarning;
2023-03-11 03:20:04 +08:00
struct GlobalTypes;
struct TypeChecker;
struct FileResolver;
struct ModuleResolver;
struct ParseResult;
2022-02-18 08:41:20 +08:00
struct HotComment;
2023-05-06 03:57:12 +08:00
struct BuildQueueItem;
2023-07-14 23:57:16 +08:00
struct FrontendCancellationToken;
2024-07-26 08:10:42 +08:00
struct AnyTypeSummary;
struct LoadDefinitionFileResult
{
bool success;
ParseResult parseResult;
2023-03-11 03:20:04 +08:00
SourceModule sourceModule;
ModulePtr module;
};
2022-02-18 08:41:20 +08:00
std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments);
std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr);
// Exported only for convenient testing.
std::optional<ModuleName> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& expr);
/** Try to convert an AST fragment into a ModuleName.
* Returns std::nullopt if the expression cannot be resolved. This will most likely happen in cases where
* the import path involves some dynamic computation that we cannot see into at typechecking time.
*
* Unintuitively, weirdly-formulated modules (like game.Parent.Parent.Parent.Foo) will successfully produce a ModuleName
* as long as it falls within the permitted syntax. This is ok because we will fail to find the module and produce an
* error when we try during typechecking.
*/
std::optional<ModuleName> pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& expr);
2023-04-28 19:55:55 +08:00
struct SourceNode
{
2022-04-22 05:04:22 +08:00
bool hasDirtySourceModule() const
{
return dirtySourceModule;
}
bool hasDirtyModule(bool forAutocomplete) const
2022-04-08 04:53:47 +08:00
{
2022-05-27 04:33:48 +08:00
return forAutocomplete ? dirtyModuleForAutocomplete : dirtyModule;
2022-04-08 04:53:47 +08:00
}
ModuleName name;
2023-04-22 05:41:03 +08:00
std::string humanReadableName;
2023-11-11 02:05:48 +08:00
DenseHashSet<ModuleName> requireSet{{}};
std::vector<std::pair<ModuleName, Location>> requireLocations;
2022-04-22 05:04:22 +08:00
bool dirtySourceModule = true;
bool dirtyModule = true;
bool dirtyModuleForAutocomplete = true;
2022-04-15 05:57:15 +08:00
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;
2022-10-07 07:55:58 +08:00
// Run typechecking only in mode required for autocomplete (strict mode in
// order to get more precise type information)
2022-04-08 04:53:47 +08:00
bool forAutocomplete = false;
2022-10-07 07:55:58 +08:00
2023-03-17 22:59:30 +08:00
bool runLintChecks = false;
2022-10-07 07:55:58 +08:00
// If not empty, randomly shuffle the constraint set before attempting to
// solve. Use this value to seed the random number generator.
std::optional<unsigned> randomizeConstraintResolutionSeed;
2023-03-17 22:59:30 +08:00
std::optional<LintOptions> enabledLintWarnings;
2023-07-14 23:57:16 +08:00
std::shared_ptr<FrontendCancellationToken> cancellationToken;
2023-08-19 01:06:29 +08:00
// Time limit for typechecking a single module
std::optional<double> moduleTimeLimitSec;
// When true, some internal complexity limits will be scaled down for modules that miss the limit set by moduleTimeLimitSec
bool applyInternalLimitScaling = false;
2024-08-23 22:24:33 +08:00
// 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<void(const SourceModule& sourceModule, const Luau::Module& module)> customModuleCheck;
};
struct CheckResult
{
std::vector<TypeError> errors;
2023-03-17 22:59:30 +08:00
LintResult lintResult;
2022-04-08 04:53:47 +08:00
std::vector<ModuleName> timeoutHits;
};
struct FrontendModuleResolver : ModuleResolver
{
FrontendModuleResolver(Frontend* frontend);
const ModulePtr getModule(const ModuleName& moduleName) const override;
bool moduleExists(const ModuleName& moduleName) const override;
std::optional<ModuleInfo> resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override;
std::string getHumanReadableModuleName(const ModuleName& moduleName) const override;
2023-04-22 05:41:03 +08:00
void setModule(const ModuleName& moduleName, ModulePtr module);
void clearModules();
private:
Frontend* frontend;
2023-04-22 05:41:03 +08:00
mutable std::mutex moduleMutex;
std::unordered_map<ModuleName, ModulePtr> 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 = {});
2023-07-08 01:14:35 +08:00
// Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking
void parse(const ModuleName& name);
// Parse and typecheck module graph
2022-01-07 06:10:07 +08:00
CheckResult check(const ModuleName& name, std::optional<FrontendOptions> optionOverride = {}); // new shininess
2022-04-08 04:53:47 +08:00
bool isDirty(const ModuleName& name, bool forAutocomplete = false) const;
void markDirty(const ModuleName& name, std::vector<ModuleName>* 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);
2023-03-11 03:20:04 +08:00
ScopePtr getEnvironmentScope(const std::string& environmentName) const;
2023-04-08 03:56:27 +08:00
void registerBuiltinDefinition(const std::string& name, std::function<void(Frontend&, GlobalTypes&, ScopePtr)>);
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
2024-08-02 07:25:12 +08:00
LoadDefinitionFileResult loadDefinitionFile(
GlobalTypes& globals,
ScopePtr targetScope,
std::string_view source,
const std::string& packageName,
bool captureComments,
bool typeCheckForAutocomplete = false
);
2022-07-01 07:29:02 +08:00
2023-05-06 03:57:12 +08:00
// 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<ModuleName>& names);
void queueModuleCheck(const ModuleName& name);
2024-08-02 07:25:12 +08:00
std::vector<ModuleName> checkQueuedModules(
std::optional<FrontendOptions> optionOverride = {},
std::function<void(std::function<void()> task)> executeTask = {},
std::function<bool(size_t done, size_t total)> progress = {}
);
2023-05-06 03:57:12 +08:00
std::optional<CheckResult> getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false);
private:
2024-08-02 07:25:12 +08:00
ModulePtr check(
const SourceModule& sourceModule,
Mode mode,
std::vector<RequireCycle> requireCycles,
std::optional<ScopePtr> environmentScope,
bool forAutocomplete,
bool recordJsonLog,
TypeCheckLimits typeCheckLimits
);
2022-06-04 04:32:20 +08:00
2023-01-04 01:33:19 +08:00
std::pair<SourceNode*, SourceModule*> getSourceNode(const ModuleName& name);
SourceModule parse(const ModuleName& name, std::string_view src, const ParseOptions& parseOptions);
2023-05-06 03:57:12 +08:00
bool parseGraph(
2024-08-02 07:25:12 +08:00
std::vector<ModuleName>& buildQueue,
const ModuleName& root,
bool forAutocomplete,
std::function<bool(const ModuleName&)> canSkip = {}
);
void addBuildQueueItems(
std::vector<BuildQueueItem>& items,
std::vector<ModuleName>& buildQueue,
bool cycleDetected,
DenseHashSet<Luau::ModuleName>& seen,
const FrontendOptions& frontendOptions
);
2023-05-06 03:57:12 +08:00
void checkBuildQueueItem(BuildQueueItem& item);
void checkBuildQueueItems(std::vector<BuildQueueItem>& items);
void recordItemResult(const BuildQueueItem& item);
static LintResult classifyLints(const std::vector<LintWarning>& warnings, const Config& config);
2023-03-11 03:20:04 +08:00
ScopePtr getModuleEnvironment(const SourceModule& module, const Config& config, bool forAutocomplete) const;
std::unordered_map<std::string, ScopePtr> environments;
2023-04-08 03:56:27 +08:00
std::unordered_map<std::string, std::function<void(Frontend&, GlobalTypes&, ScopePtr)>> builtinDefinitions;
2023-01-04 01:33:19 +08:00
BuiltinTypes builtinTypes_;
2022-07-01 07:29:02 +08:00
public:
2023-01-04 01:33:19 +08:00
const NotNull<BuiltinTypes> builtinTypes;
2022-09-09 05:44:50 +08:00
FileResolver* fileResolver;
2023-04-14 20:05:27 +08:00
FrontendModuleResolver moduleResolver;
FrontendModuleResolver moduleResolverForAutocomplete;
2023-04-14 20:05:27 +08:00
2023-03-11 03:20:04 +08:00
GlobalTypes globals;
GlobalTypes globalsForAutocomplete;
2023-04-14 20:05:27 +08:00
ConfigResolver* configResolver;
FrontendOptions options;
InternalErrorReporter iceHandler;
2023-04-14 20:05:27 +08:00
std::function<void(const ModuleName& name, const ScopePtr& scope, bool forAutocomplete)> prepareModuleScope;
2024-01-19 23:13:08 +08:00
std::function<void(const ModuleName& name, std::string log)> writeJsonLog = {};
2023-05-06 03:57:12 +08:00
std::unordered_map<ModuleName, std::shared_ptr<SourceNode>> sourceNodes;
std::unordered_map<ModuleName, std::shared_ptr<SourceModule>> sourceModules;
2022-06-17 08:54:42 +08:00
std::unordered_map<ModuleName, RequireTraceResult> requireTrace;
Stats stats = {};
2023-05-06 03:57:12 +08:00
std::vector<ModuleName> moduleQueue;
};
2024-08-02 07:25:12 +08:00
ModulePtr check(
const SourceModule& sourceModule,
Mode mode,
const std::vector<RequireCycle>& requireCycles,
NotNull<BuiltinTypes> builtinTypes,
NotNull<InternalErrorReporter> iceHandler,
NotNull<ModuleResolver> moduleResolver,
NotNull<FileResolver> fileResolver,
const ScopePtr& globalScope,
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
FrontendOptions options,
TypeCheckLimits limits
);
ModulePtr check(
const SourceModule& sourceModule,
Mode mode,
const std::vector<RequireCycle>& requireCycles,
NotNull<BuiltinTypes> builtinTypes,
NotNull<InternalErrorReporter> iceHandler,
NotNull<ModuleResolver> moduleResolver,
NotNull<FileResolver> fileResolver,
const ScopePtr& globalScope,
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope,
FrontendOptions options,
TypeCheckLimits limits,
bool recordJsonLog,
std::function<void(const ModuleName&, std::string)> writeJsonLog
);
2023-02-17 22:53:37 +08:00
} // namespace Luau