mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-13 21:40:43 +00:00
Merge branch 'upstream' into merge
This commit is contained in:
commit
1f679b08b8
84 changed files with 1497 additions and 579 deletions
|
@ -21,6 +21,7 @@ struct BasicDocumentation
|
||||||
{
|
{
|
||||||
std::string documentation;
|
std::string documentation;
|
||||||
std::string learnMoreLink;
|
std::string learnMoreLink;
|
||||||
|
std::string codeSample;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FunctionParameterDocumentation
|
struct FunctionParameterDocumentation
|
||||||
|
@ -37,6 +38,7 @@ struct FunctionDocumentation
|
||||||
std::vector<FunctionParameterDocumentation> parameters;
|
std::vector<FunctionParameterDocumentation> parameters;
|
||||||
std::vector<DocumentationSymbol> returns;
|
std::vector<DocumentationSymbol> returns;
|
||||||
std::string learnMoreLink;
|
std::string learnMoreLink;
|
||||||
|
std::string codeSample;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OverloadedFunctionDocumentation
|
struct OverloadedFunctionDocumentation
|
||||||
|
@ -52,6 +54,7 @@ struct TableDocumentation
|
||||||
std::string documentation;
|
std::string documentation;
|
||||||
Luau::DenseHashMap<std::string, DocumentationSymbol> keys;
|
Luau::DenseHashMap<std::string, DocumentationSymbol> keys;
|
||||||
std::string learnMoreLink;
|
std::string learnMoreLink;
|
||||||
|
std::string codeSample;
|
||||||
};
|
};
|
||||||
|
|
||||||
using DocumentationDatabase = Luau::DenseHashMap<DocumentationSymbol, Documentation>;
|
using DocumentationDatabase = Luau::DenseHashMap<DocumentationSymbol, Documentation>;
|
||||||
|
|
|
@ -24,6 +24,7 @@ struct TypeChecker;
|
||||||
struct FileResolver;
|
struct FileResolver;
|
||||||
struct ModuleResolver;
|
struct ModuleResolver;
|
||||||
struct ParseResult;
|
struct ParseResult;
|
||||||
|
struct HotComment;
|
||||||
|
|
||||||
struct LoadDefinitionFileResult
|
struct LoadDefinitionFileResult
|
||||||
{
|
{
|
||||||
|
@ -35,7 +36,7 @@ struct LoadDefinitionFileResult
|
||||||
LoadDefinitionFileResult loadDefinitionFile(
|
LoadDefinitionFileResult loadDefinitionFile(
|
||||||
TypeChecker& typeChecker, ScopePtr targetScope, std::string_view definition, const std::string& packageName);
|
TypeChecker& typeChecker, ScopePtr targetScope, std::string_view definition, const std::string& packageName);
|
||||||
|
|
||||||
std::optional<Mode> parseMode(const std::vector<std::string>& hotcomments);
|
std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments);
|
||||||
|
|
||||||
std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr);
|
std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ class AstStat;
|
||||||
class AstNameTable;
|
class AstNameTable;
|
||||||
struct TypeChecker;
|
struct TypeChecker;
|
||||||
struct Module;
|
struct Module;
|
||||||
|
struct HotComment;
|
||||||
|
|
||||||
using ScopePtr = std::shared_ptr<struct Scope>;
|
using ScopePtr = std::shared_ptr<struct Scope>;
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ struct LintWarning
|
||||||
Code_TableOperations = 23,
|
Code_TableOperations = 23,
|
||||||
Code_DuplicateCondition = 24,
|
Code_DuplicateCondition = 24,
|
||||||
Code_MisleadingAndOr = 25,
|
Code_MisleadingAndOr = 25,
|
||||||
|
Code_CommentDirective = 26,
|
||||||
|
|
||||||
Code__Count
|
Code__Count
|
||||||
};
|
};
|
||||||
|
@ -60,7 +62,7 @@ struct LintWarning
|
||||||
|
|
||||||
static const char* getName(Code code);
|
static const char* getName(Code code);
|
||||||
static Code parseName(const char* name);
|
static Code parseName(const char* name);
|
||||||
static uint64_t parseMask(const std::vector<std::string>& hotcomments);
|
static uint64_t parseMask(const std::vector<HotComment>& hotcomments);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LintResult
|
struct LintResult
|
||||||
|
@ -90,7 +92,8 @@ struct LintOptions
|
||||||
void setDefaults();
|
void setDefaults();
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const ScopePtr& env, const Module* module, const LintOptions& options);
|
std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const ScopePtr& env, const Module* module,
|
||||||
|
const std::vector<HotComment>& hotcomments, const LintOptions& options);
|
||||||
|
|
||||||
std::vector<AstName> getDeprecatedGlobals(const AstNameTable& names);
|
std::vector<AstName> getDeprecatedGlobals(const AstNameTable& names);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "Luau/TypedAllocator.h"
|
#include "Luau/TypedAllocator.h"
|
||||||
#include "Luau/ParseOptions.h"
|
#include "Luau/ParseOptions.h"
|
||||||
#include "Luau/Error.h"
|
#include "Luau/Error.h"
|
||||||
#include "Luau/Parser.h"
|
#include "Luau/ParseResult.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -37,8 +37,8 @@ struct SourceModule
|
||||||
|
|
||||||
AstStatBlock* root = nullptr;
|
AstStatBlock* root = nullptr;
|
||||||
std::optional<Mode> mode;
|
std::optional<Mode> mode;
|
||||||
uint64_t ignoreLints = 0;
|
|
||||||
|
|
||||||
|
std::vector<HotComment> hotcomments;
|
||||||
std::vector<Comment> commentLocations;
|
std::vector<Comment> commentLocations;
|
||||||
|
|
||||||
SourceModule()
|
SourceModule()
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Module;
|
void quantify(TypeId ty, TypeLevel level);
|
||||||
using ModulePtr = std::shared_ptr<Module>;
|
|
||||||
|
|
||||||
void quantify(ModulePtr module, TypeId ty, TypeLevel level);
|
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -101,9 +101,6 @@ struct Tarjan
|
||||||
// This is hot code, so we optimize recursion to a stack.
|
// This is hot code, so we optimize recursion to a stack.
|
||||||
TarjanResult loop();
|
TarjanResult loop();
|
||||||
|
|
||||||
// Clear the state
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
// Find or create the index for a vertex.
|
// Find or create the index for a vertex.
|
||||||
// Return a boolean which is `true` if it's a freshly created index.
|
// Return a boolean which is `true` if it's a freshly created index.
|
||||||
std::pair<int, bool> indexify(TypeId ty);
|
std::pair<int, bool> indexify(TypeId ty);
|
||||||
|
@ -166,7 +163,17 @@ struct FindDirty : Tarjan
|
||||||
// and replaces them with clean ones.
|
// and replaces them with clean ones.
|
||||||
struct Substitution : FindDirty
|
struct Substitution : FindDirty
|
||||||
{
|
{
|
||||||
ModulePtr currentModule;
|
protected:
|
||||||
|
Substitution(const TxnLog* log_, TypeArena* arena)
|
||||||
|
: arena(arena)
|
||||||
|
{
|
||||||
|
log = log_;
|
||||||
|
LUAU_ASSERT(log);
|
||||||
|
LUAU_ASSERT(arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TypeArena* arena;
|
||||||
DenseHashMap<TypeId, TypeId> newTypes{nullptr};
|
DenseHashMap<TypeId, TypeId> newTypes{nullptr};
|
||||||
DenseHashMap<TypePackId, TypePackId> newPacks{nullptr};
|
DenseHashMap<TypePackId, TypePackId> newPacks{nullptr};
|
||||||
|
|
||||||
|
@ -192,12 +199,13 @@ struct Substitution : FindDirty
|
||||||
template<typename T>
|
template<typename T>
|
||||||
TypeId addType(const T& tv)
|
TypeId addType(const T& tv)
|
||||||
{
|
{
|
||||||
return currentModule->internalTypes.addType(tv);
|
return arena->addType(tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
TypePackId addTypePack(const T& tp)
|
TypePackId addTypePack(const T& tp)
|
||||||
{
|
{
|
||||||
return currentModule->internalTypes.addTypePack(TypePackVar{tp});
|
return arena->addTypePack(TypePackVar{tp});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include "Luau/Error.h"
|
#include "Luau/Error.h"
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
#include "Luau/Symbol.h"
|
#include "Luau/Symbol.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/Substitution.h"
|
#include "Luau/Substitution.h"
|
||||||
#include "Luau/TxnLog.h"
|
#include "Luau/TxnLog.h"
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
|
@ -37,6 +36,15 @@ struct Unifier;
|
||||||
// A substitution which replaces generic types in a given set by free types.
|
// A substitution which replaces generic types in a given set by free types.
|
||||||
struct ReplaceGenerics : Substitution
|
struct ReplaceGenerics : Substitution
|
||||||
{
|
{
|
||||||
|
ReplaceGenerics(
|
||||||
|
const TxnLog* log, TypeArena* arena, TypeLevel level, const std::vector<TypeId>& generics, const std::vector<TypePackId>& genericPacks)
|
||||||
|
: Substitution(log, arena)
|
||||||
|
, level(level)
|
||||||
|
, generics(generics)
|
||||||
|
, genericPacks(genericPacks)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
std::vector<TypeId> generics;
|
std::vector<TypeId> generics;
|
||||||
std::vector<TypePackId> genericPacks;
|
std::vector<TypePackId> genericPacks;
|
||||||
|
@ -50,8 +58,13 @@ struct ReplaceGenerics : Substitution
|
||||||
// A substitution which replaces generic functions by monomorphic functions
|
// A substitution which replaces generic functions by monomorphic functions
|
||||||
struct Instantiation : Substitution
|
struct Instantiation : Substitution
|
||||||
{
|
{
|
||||||
|
Instantiation(const TxnLog* log, TypeArena* arena, TypeLevel level)
|
||||||
|
: Substitution(log, arena)
|
||||||
|
, level(level)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
ReplaceGenerics replaceGenerics;
|
|
||||||
bool ignoreChildren(TypeId ty) override;
|
bool ignoreChildren(TypeId ty) override;
|
||||||
bool isDirty(TypeId ty) override;
|
bool isDirty(TypeId ty) override;
|
||||||
bool isDirty(TypePackId tp) override;
|
bool isDirty(TypePackId tp) override;
|
||||||
|
@ -62,6 +75,12 @@ struct Instantiation : Substitution
|
||||||
// A substitution which replaces free types by generic types.
|
// A substitution which replaces free types by generic types.
|
||||||
struct Quantification : Substitution
|
struct Quantification : Substitution
|
||||||
{
|
{
|
||||||
|
Quantification(TypeArena* arena, TypeLevel level)
|
||||||
|
: Substitution(TxnLog::empty(), arena)
|
||||||
|
, level(level)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
std::vector<TypeId> generics;
|
std::vector<TypeId> generics;
|
||||||
std::vector<TypePackId> genericPacks;
|
std::vector<TypePackId> genericPacks;
|
||||||
|
@ -74,6 +93,13 @@ struct Quantification : Substitution
|
||||||
// A substitution which replaces free types by any
|
// A substitution which replaces free types by any
|
||||||
struct Anyification : Substitution
|
struct Anyification : Substitution
|
||||||
{
|
{
|
||||||
|
Anyification(TypeArena* arena, TypeId anyType, TypePackId anyTypePack)
|
||||||
|
: Substitution(TxnLog::empty(), arena)
|
||||||
|
, anyType(anyType)
|
||||||
|
, anyTypePack(anyTypePack)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
TypeId anyType;
|
TypeId anyType;
|
||||||
TypePackId anyTypePack;
|
TypePackId anyTypePack;
|
||||||
bool isDirty(TypeId ty) override;
|
bool isDirty(TypeId ty) override;
|
||||||
|
@ -85,6 +111,13 @@ struct Anyification : Substitution
|
||||||
// A substitution which replaces the type parameters of a type function by arguments
|
// A substitution which replaces the type parameters of a type function by arguments
|
||||||
struct ApplyTypeFunction : Substitution
|
struct ApplyTypeFunction : Substitution
|
||||||
{
|
{
|
||||||
|
ApplyTypeFunction(TypeArena* arena, TypeLevel level)
|
||||||
|
: Substitution(TxnLog::empty(), arena)
|
||||||
|
, level(level)
|
||||||
|
, encounteredForwardedType(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
bool encounteredForwardedType;
|
bool encounteredForwardedType;
|
||||||
std::unordered_map<TypeId, TypeId> typeArguments;
|
std::unordered_map<TypeId, TypeId> typeArguments;
|
||||||
|
@ -351,8 +384,7 @@ private:
|
||||||
|
|
||||||
// Note: `scope` must be a fresh scope.
|
// Note: `scope` must be a fresh scope.
|
||||||
GenericTypeDefinitions createGenericTypes(const ScopePtr& scope, std::optional<TypeLevel> levelOpt, const AstNode& node,
|
GenericTypeDefinitions createGenericTypes(const ScopePtr& scope, std::optional<TypeLevel> levelOpt, const AstNode& node,
|
||||||
const AstArray<AstGenericType>& genericNames, const AstArray<AstGenericTypePack>& genericPackNames,
|
const AstArray<AstGenericType>& genericNames, const AstArray<AstGenericTypePack>& genericPackNames, bool useCache = false);
|
||||||
bool useCache = false);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ErrorVec resolve(const PredicateVec& predicates, const ScopePtr& scope, bool sense);
|
ErrorVec resolve(const PredicateVec& predicates, const ScopePtr& scope, bool sense);
|
||||||
|
@ -392,11 +424,6 @@ public:
|
||||||
ModulePtr currentModule;
|
ModulePtr currentModule;
|
||||||
ModuleName currentModuleName;
|
ModuleName currentModuleName;
|
||||||
|
|
||||||
Instantiation instantiation;
|
|
||||||
Quantification quantification;
|
|
||||||
Anyification anyification;
|
|
||||||
ApplyTypeFunction applyTypeFunction;
|
|
||||||
|
|
||||||
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
|
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
|
||||||
InternalErrorReporter* iceHandler;
|
InternalErrorReporter* iceHandler;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
|
#include "Luau/Parser.h" // TODO: only needed for autocompleteSource which is deprecated
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
@ -14,9 +15,9 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAutocompleteAvoidMutation, false);
|
LUAU_FASTFLAGVARIABLE(LuauAutocompleteAvoidMutation, false);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompleteBrokenStringParams, false);
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMissingFollowACMetatables, false);
|
LUAU_FASTFLAGVARIABLE(LuauMissingFollowACMetatables, false);
|
||||||
LUAU_FASTFLAGVARIABLE(PreferToCallFunctionsForIntersects, false);
|
LUAU_FASTFLAGVARIABLE(PreferToCallFunctionsForIntersects, false);
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauIfElseExprFixCompletionIssue, false);
|
||||||
|
|
||||||
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
static const std::unordered_set<std::string> kStatementStartingKeywords = {
|
||||||
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
|
||||||
|
@ -380,7 +381,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
|
||||||
{
|
{
|
||||||
// We are walking up the class hierarchy, so if we encounter a property that we have
|
// We are walking up the class hierarchy, so if we encounter a property that we have
|
||||||
// already populated, it takes precedence over the property we found just now.
|
// already populated, it takes precedence over the property we found just now.
|
||||||
if (result.count(name) == 0 && name != Parser::errorName)
|
if (result.count(name) == 0 && name != kParseNameError)
|
||||||
{
|
{
|
||||||
Luau::TypeId type = Luau::follow(prop.type);
|
Luau::TypeId type = Luau::follow(prop.type);
|
||||||
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key ? TypeCorrectKind::Correct
|
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key ? TypeCorrectKind::Correct
|
||||||
|
@ -948,9 +949,12 @@ AutocompleteEntryMap autocompleteTypeNames(const Module& module, Position positi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < node->returnAnnotation.types.size; i++)
|
if (!node->returnAnnotation)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < node->returnAnnotation->types.size; i++)
|
||||||
{
|
{
|
||||||
AstType* ret = node->returnAnnotation.types.data[i];
|
AstType* ret = node->returnAnnotation->types.data[i];
|
||||||
|
|
||||||
if (ret->location.containsClosed(position))
|
if (ret->location.containsClosed(position))
|
||||||
{
|
{
|
||||||
|
@ -965,7 +969,7 @@ AutocompleteEntryMap autocompleteTypeNames(const Module& module, Position positi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AstTypePack* retTp = node->returnAnnotation.tailType)
|
if (AstTypePack* retTp = node->returnAnnotation->tailType)
|
||||||
{
|
{
|
||||||
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
if (auto variadic = retTp->as<AstTypePackVariadic>())
|
||||||
{
|
{
|
||||||
|
@ -1136,7 +1140,7 @@ static AutocompleteEntryMap autocompleteStatement(
|
||||||
AstNode* parent = ancestry.rbegin()[1];
|
AstNode* parent = ancestry.rbegin()[1];
|
||||||
if (AstStatIf* statIf = parent->as<AstStatIf>())
|
if (AstStatIf* statIf = parent->as<AstStatIf>())
|
||||||
{
|
{
|
||||||
if (!statIf->elsebody || (statIf->hasElse && statIf->elseLocation.containsClosed(position)))
|
if (!statIf->elsebody || (statIf->elseLocation && statIf->elseLocation->containsClosed(position)))
|
||||||
{
|
{
|
||||||
result.emplace("else", AutocompleteEntry{AutocompleteEntryKind::Keyword});
|
result.emplace("else", AutocompleteEntry{AutocompleteEntryKind::Keyword});
|
||||||
result.emplace("elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword});
|
result.emplace("elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword});
|
||||||
|
@ -1164,8 +1168,7 @@ static AutocompleteEntryMap autocompleteStatement(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if completions were generated (completions will be inserted into 'outResult')
|
// Returns true iff `node` was handled by this function (completions, if any, are returned in `outResult`)
|
||||||
// Returns false if no completions were generated
|
|
||||||
static bool autocompleteIfElseExpression(
|
static bool autocompleteIfElseExpression(
|
||||||
const AstNode* node, const std::vector<AstNode*>& ancestry, const Position& position, AutocompleteEntryMap& outResult)
|
const AstNode* node, const std::vector<AstNode*>& ancestry, const Position& position, AutocompleteEntryMap& outResult)
|
||||||
{
|
{
|
||||||
|
@ -1173,6 +1176,13 @@ static bool autocompleteIfElseExpression(
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (FFlag::LuauIfElseExprFixCompletionIssue && node->is<AstExprIfElse>())
|
||||||
|
{
|
||||||
|
// Don't try to complete when the current node is an if-else expression (i.e. only try to complete when the node is a child of an if-else
|
||||||
|
// expression.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
AstExprIfElse* ifElseExpr = parent->as<AstExprIfElse>();
|
AstExprIfElse* ifElseExpr = parent->as<AstExprIfElse>();
|
||||||
if (!ifElseExpr || ifElseExpr->condition->location.containsClosed(position))
|
if (!ifElseExpr || ifElseExpr->condition->location.containsClosed(position))
|
||||||
{
|
{
|
||||||
|
@ -1310,7 +1320,7 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(const Source
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nodes.back()->is<AstExprConstantString>() && (!FFlag::LuauCompleteBrokenStringParams || !nodes.back()->is<AstExprError>()))
|
if (!nodes.back()->is<AstExprConstantString>() && !nodes.back()->is<AstExprError>())
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -1408,8 +1418,8 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||||
}
|
}
|
||||||
else if (auto typeReference = node->as<AstTypeReference>())
|
else if (auto typeReference = node->as<AstTypeReference>())
|
||||||
{
|
{
|
||||||
if (typeReference->hasPrefix)
|
if (typeReference->prefix)
|
||||||
return {autocompleteModuleTypes(*module, position, typeReference->prefix.value), finder.ancestry};
|
return {autocompleteModuleTypes(*module, position, typeReference->prefix->value), finder.ancestry};
|
||||||
else
|
else
|
||||||
return {autocompleteTypeNames(*module, position, finder.ancestry), finder.ancestry};
|
return {autocompleteTypeNames(*module, position, finder.ancestry), finder.ancestry};
|
||||||
}
|
}
|
||||||
|
@ -1419,9 +1429,9 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||||
}
|
}
|
||||||
else if (AstStatLocal* statLocal = node->as<AstStatLocal>())
|
else if (AstStatLocal* statLocal = node->as<AstStatLocal>())
|
||||||
{
|
{
|
||||||
if (statLocal->vars.size == 1 && (!statLocal->hasEqualsSign || position < statLocal->equalsSignLocation.begin))
|
if (statLocal->vars.size == 1 && (!statLocal->equalsSignLocation || position < statLocal->equalsSignLocation->begin))
|
||||||
return {{{"function", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
return {{{"function", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
||||||
else if (statLocal->hasEqualsSign && position >= statLocal->equalsSignLocation.end)
|
else if (statLocal->equalsSignLocation && position >= statLocal->equalsSignLocation->end)
|
||||||
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
|
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
|
||||||
else
|
else
|
||||||
return {};
|
return {};
|
||||||
|
@ -1449,7 +1459,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||||
if (!statForIn->hasIn || position <= statForIn->inLocation.begin)
|
if (!statForIn->hasIn || position <= statForIn->inLocation.begin)
|
||||||
{
|
{
|
||||||
AstLocal* lastName = statForIn->vars.data[statForIn->vars.size - 1];
|
AstLocal* lastName = statForIn->vars.data[statForIn->vars.size - 1];
|
||||||
if (lastName->name == Parser::errorName || lastName->location.containsClosed(position))
|
if (lastName->name == kParseNameError || lastName->location.containsClosed(position))
|
||||||
{
|
{
|
||||||
// Here we are either working with a missing binding (as would be the case in a bare "for" keyword) or
|
// Here we are either working with a missing binding (as would be the case in a bare "for" keyword) or
|
||||||
// the cursor is still touching a binding name. The user is still typing a new name, so we should not offer
|
// the cursor is still touching a binding name. The user is still typing a new name, so we should not offer
|
||||||
|
@ -1499,7 +1509,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||||
else if (AstStatWhile* statWhile = extractStat<AstStatWhile>(finder.ancestry); statWhile && !statWhile->hasDo)
|
else if (AstStatWhile* statWhile = extractStat<AstStatWhile>(finder.ancestry); statWhile && !statWhile->hasDo)
|
||||||
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
||||||
|
|
||||||
else if (AstStatIf* statIf = node->as<AstStatIf>(); statIf && !statIf->hasElse)
|
else if (AstStatIf* statIf = node->as<AstStatIf>(); statIf && !statIf->elseLocation.has_value())
|
||||||
{
|
{
|
||||||
return {{{"else", AutocompleteEntry{AutocompleteEntryKind::Keyword}}, {"elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword}}},
|
return {{{"else", AutocompleteEntry{AutocompleteEntryKind::Keyword}}, {"elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword}}},
|
||||||
finder.ancestry};
|
finder.ancestry};
|
||||||
|
@ -1508,11 +1518,11 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
|
||||||
{
|
{
|
||||||
if (statIf->condition->is<AstExprError>())
|
if (statIf->condition->is<AstExprError>())
|
||||||
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
|
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
|
||||||
else if (!statIf->hasThen || statIf->thenLocation.containsClosed(position))
|
else if (!statIf->thenLocation || statIf->thenLocation->containsClosed(position))
|
||||||
return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
||||||
}
|
}
|
||||||
else if (AstStatIf* statIf = extractStat<AstStatIf>(finder.ancestry);
|
else if (AstStatIf* statIf = extractStat<AstStatIf>(finder.ancestry);
|
||||||
statIf && (!statIf->hasThen || statIf->thenLocation.containsClosed(position)))
|
statIf && (!statIf->thenLocation || statIf->thenLocation->containsClosed(position)))
|
||||||
return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, finder.ancestry};
|
||||||
else if (AstStatRepeat* statRepeat = node->as<AstStatRepeat>(); statRepeat && statRepeat->condition->is<AstExprError>())
|
else if (AstStatRepeat* statRepeat = node->as<AstStatRepeat>(); statRepeat && statRepeat->condition->is<AstExprError>())
|
||||||
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
|
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
|
||||||
|
@ -1612,6 +1622,7 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
|
||||||
|
|
||||||
OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view source, Position position, StringCompletionCallback callback)
|
OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view source, Position position, StringCompletionCallback callback)
|
||||||
{
|
{
|
||||||
|
// TODO: Remove #include "Luau/Parser.h" with this function
|
||||||
auto sourceModule = std::make_unique<SourceModule>();
|
auto sourceModule = std::make_unique<SourceModule>();
|
||||||
ParseOptions parseOptions;
|
ParseOptions parseOptions;
|
||||||
parseOptions.captureComments = true;
|
parseOptions.captureComments = true;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Config.h"
|
#include "Luau/Config.h"
|
||||||
|
|
||||||
#include "Luau/Parser.h"
|
#include "Luau/Lexer.h"
|
||||||
#include "Luau/StringUtils.h"
|
#include "Luau/StringUtils.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
|
@ -167,7 +167,7 @@ declare function gcinfo(): number
|
||||||
foreach: <K, V>({[K]: V}, (K, V) -> ()) -> (),
|
foreach: <K, V>({[K]: V}, (K, V) -> ()) -> (),
|
||||||
foreachi: <V>({V}, (number, V) -> ()) -> (),
|
foreachi: <V>({V}, (number, V) -> ()) -> (),
|
||||||
|
|
||||||
move: <V>({V}, number, number, number, {V}?) -> (),
|
move: <V>({V}, number, number, number, {V}?) -> {V},
|
||||||
clear: <K, V>({[K]: V}) -> (),
|
clear: <K, V>({[K]: V}) -> (),
|
||||||
|
|
||||||
freeze: <K, V>({[K]: V}) -> {[K]: V},
|
freeze: <K, V>({[K]: V}) -> {[K]: V},
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/Config.h"
|
#include "Luau/Config.h"
|
||||||
#include "Luau/FileResolver.h"
|
#include "Luau/FileResolver.h"
|
||||||
|
#include "Luau/Parser.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/StringUtils.h"
|
#include "Luau/StringUtils.h"
|
||||||
#include "Luau/TimeTrace.h"
|
#include "Luau/TimeTrace.h"
|
||||||
|
@ -16,23 +17,25 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
LUAU_FASTFLAG(LuauInferInNoCheckMode)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckTwice, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
|
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
std::optional<Mode> parseMode(const std::vector<std::string>& hotcomments)
|
std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments)
|
||||||
{
|
{
|
||||||
for (const std::string& hc : hotcomments)
|
for (const HotComment& hc : hotcomments)
|
||||||
{
|
{
|
||||||
if (hc == "nocheck")
|
if (!hc.header)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (hc.content == "nocheck")
|
||||||
return Mode::NoCheck;
|
return Mode::NoCheck;
|
||||||
|
|
||||||
if (hc == "nonstrict")
|
if (hc.content == "nonstrict")
|
||||||
return Mode::Nonstrict;
|
return Mode::Nonstrict;
|
||||||
|
|
||||||
if (hc == "strict")
|
if (hc.content == "strict")
|
||||||
return Mode::Strict;
|
return Mode::Strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,13 +610,15 @@ std::pair<SourceModule, LintResult> Frontend::lintFragment(std::string_view sour
|
||||||
|
|
||||||
SourceModule sourceModule = parse(ModuleName{}, source, config.parseOptions);
|
SourceModule sourceModule = parse(ModuleName{}, source, config.parseOptions);
|
||||||
|
|
||||||
|
uint64_t ignoreLints = LintWarning::parseMask(sourceModule.hotcomments);
|
||||||
|
|
||||||
Luau::LintOptions lintOptions = enabledLintWarnings.value_or(config.enabledLint);
|
Luau::LintOptions lintOptions = enabledLintWarnings.value_or(config.enabledLint);
|
||||||
lintOptions.warningMask &= sourceModule.ignoreLints;
|
lintOptions.warningMask &= ~ignoreLints;
|
||||||
|
|
||||||
double timestamp = getTimestamp();
|
double timestamp = getTimestamp();
|
||||||
|
|
||||||
std::vector<LintWarning> warnings =
|
std::vector<LintWarning> warnings = Luau::lint(sourceModule.root, *sourceModule.names.get(), typeChecker.globalScope, nullptr,
|
||||||
Luau::lint(sourceModule.root, *sourceModule.names.get(), typeChecker.globalScope, nullptr, enabledLintWarnings.value_or(config.enabledLint));
|
sourceModule.hotcomments, enabledLintWarnings.value_or(config.enabledLint));
|
||||||
|
|
||||||
stats.timeLint += getTimestamp() - timestamp;
|
stats.timeLint += getTimestamp() - timestamp;
|
||||||
|
|
||||||
|
@ -651,8 +656,10 @@ LintResult Frontend::lint(const SourceModule& module, std::optional<Luau::LintOp
|
||||||
|
|
||||||
const Config& config = configResolver->getConfig(module.name);
|
const Config& config = configResolver->getConfig(module.name);
|
||||||
|
|
||||||
|
uint64_t ignoreLints = LintWarning::parseMask(module.hotcomments);
|
||||||
|
|
||||||
LintOptions options = enabledLintWarnings.value_or(config.enabledLint);
|
LintOptions options = enabledLintWarnings.value_or(config.enabledLint);
|
||||||
options.warningMask &= ~module.ignoreLints;
|
options.warningMask &= ~ignoreLints;
|
||||||
|
|
||||||
Mode mode = module.mode.value_or(config.mode);
|
Mode mode = module.mode.value_or(config.mode);
|
||||||
if (mode != Mode::NoCheck)
|
if (mode != Mode::NoCheck)
|
||||||
|
@ -671,7 +678,7 @@ LintResult Frontend::lint(const SourceModule& module, std::optional<Luau::LintOp
|
||||||
|
|
||||||
double timestamp = getTimestamp();
|
double timestamp = getTimestamp();
|
||||||
|
|
||||||
std::vector<LintWarning> warnings = Luau::lint(module.root, *module.names, environmentScope, modulePtr.get(), options);
|
std::vector<LintWarning> warnings = Luau::lint(module.root, *module.names, environmentScope, modulePtr.get(), module.hotcomments, options);
|
||||||
|
|
||||||
stats.timeLint += getTimestamp() - timestamp;
|
stats.timeLint += getTimestamp() - timestamp;
|
||||||
|
|
||||||
|
@ -839,7 +846,6 @@ SourceModule Frontend::parse(const ModuleName& name, std::string_view src, const
|
||||||
{
|
{
|
||||||
sourceModule.root = parseResult.root;
|
sourceModule.root = parseResult.root;
|
||||||
sourceModule.mode = parseMode(parseResult.hotcomments);
|
sourceModule.mode = parseMode(parseResult.hotcomments);
|
||||||
sourceModule.ignoreLints = LintWarning::parseMask(parseResult.hotcomments);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -848,8 +854,13 @@ SourceModule Frontend::parse(const ModuleName& name, std::string_view src, const
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceModule.name = name;
|
sourceModule.name = name;
|
||||||
|
|
||||||
if (parseOptions.captureComments)
|
if (parseOptions.captureComments)
|
||||||
|
{
|
||||||
sourceModule.commentLocations = std::move(parseResult.commentLocations);
|
sourceModule.commentLocations = std::move(parseResult.commentLocations);
|
||||||
|
sourceModule.hotcomments = std::move(parseResult.hotcomments);
|
||||||
|
}
|
||||||
|
|
||||||
return sourceModule;
|
return sourceModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,13 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
{
|
{
|
||||||
writeString(str);
|
writeString(str);
|
||||||
}
|
}
|
||||||
|
void write(std::optional<AstName> name)
|
||||||
|
{
|
||||||
|
if (name)
|
||||||
|
write(*name);
|
||||||
|
else
|
||||||
|
writeRaw("null");
|
||||||
|
}
|
||||||
void write(AstName name)
|
void write(AstName name)
|
||||||
{
|
{
|
||||||
writeString(name.value ? name.value : "");
|
writeString(name.value ? name.value : "");
|
||||||
|
@ -327,7 +334,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
if (node->self)
|
if (node->self)
|
||||||
PROP(self);
|
PROP(self);
|
||||||
PROP(args);
|
PROP(args);
|
||||||
if (node->hasReturnAnnotation)
|
if (node->returnAnnotation)
|
||||||
PROP(returnAnnotation);
|
PROP(returnAnnotation);
|
||||||
PROP(vararg);
|
PROP(vararg);
|
||||||
PROP(varargLocation);
|
PROP(varargLocation);
|
||||||
|
@ -341,6 +348,14 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write(const std::optional<AstTypeList>& typeList)
|
||||||
|
{
|
||||||
|
if (typeList)
|
||||||
|
write(*typeList);
|
||||||
|
else
|
||||||
|
writeRaw("null");
|
||||||
|
}
|
||||||
|
|
||||||
void write(const AstTypeList& typeList)
|
void write(const AstTypeList& typeList)
|
||||||
{
|
{
|
||||||
writeRaw("{");
|
writeRaw("{");
|
||||||
|
@ -544,7 +559,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
PROP(thenbody);
|
PROP(thenbody);
|
||||||
if (node->elsebody)
|
if (node->elsebody)
|
||||||
PROP(elsebody);
|
PROP(elsebody);
|
||||||
PROP(hasThen);
|
write("hasThen", node->thenLocation.has_value());
|
||||||
PROP(hasEnd);
|
PROP(hasEnd);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -728,7 +743,7 @@ struct AstJsonEncoder : public AstVisitor
|
||||||
void write(class AstTypeReference* node)
|
void write(class AstTypeReference* node)
|
||||||
{
|
{
|
||||||
writeNode(node, "AstTypeReference", [&]() {
|
writeNode(node, "AstTypeReference", [&]() {
|
||||||
if (node->hasPrefix)
|
if (node->prefix)
|
||||||
PROP(prefix);
|
PROP(prefix);
|
||||||
PROP(name);
|
PROP(name);
|
||||||
PROP(parameters);
|
PROP(parameters);
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ static const char* kWarningNames[] = {
|
||||||
"TableOperations",
|
"TableOperations",
|
||||||
"DuplicateCondition",
|
"DuplicateCondition",
|
||||||
"MisleadingAndOr",
|
"MisleadingAndOr",
|
||||||
|
"CommentDirective",
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@ -732,13 +735,13 @@ private:
|
||||||
|
|
||||||
bool visit(AstTypeReference* node) override
|
bool visit(AstTypeReference* node) override
|
||||||
{
|
{
|
||||||
if (!node->hasPrefix)
|
if (!node->prefix)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!imports.contains(node->prefix))
|
if (!imports.contains(*node->prefix))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
AstLocal* astLocal = imports[node->prefix];
|
AstLocal* astLocal = imports[*node->prefix];
|
||||||
Local& local = locals[astLocal];
|
Local& local = locals[astLocal];
|
||||||
LUAU_ASSERT(local.import);
|
LUAU_ASSERT(local.import);
|
||||||
local.used = true;
|
local.used = true;
|
||||||
|
@ -2527,13 +2530,108 @@ static void fillBuiltinGlobals(LintContext& context, const AstNameTable& names,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* fuzzyMatch(std::string_view str, const char** array, size_t size)
|
||||||
|
{
|
||||||
|
if (FInt::LuauSuggestionDistance == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
size_t bestDistance = FInt::LuauSuggestionDistance;
|
||||||
|
size_t bestMatch = size;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
size_t ed = editDistance(str, array[i]);
|
||||||
|
|
||||||
|
if (ed <= bestDistance)
|
||||||
|
{
|
||||||
|
bestDistance = ed;
|
||||||
|
bestMatch = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestMatch < size ? array[bestMatch] : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lintComments(LintContext& context, const std::vector<HotComment>& hotcomments)
|
||||||
|
{
|
||||||
|
bool seenMode = false;
|
||||||
|
|
||||||
|
for (const HotComment& hc : hotcomments)
|
||||||
|
{
|
||||||
|
// We reserve --!<space> for various informational (non-directive) comments
|
||||||
|
if (hc.content.empty() || hc.content[0] == ' ' || hc.content[0] == '\t')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!hc.header)
|
||||||
|
{
|
||||||
|
emitWarning(context, LintWarning::Code_CommentDirective, hc.location,
|
||||||
|
"Comment directive is ignored because it is placed after the first non-comment token");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string::size_type space = hc.content.find_first_of(" \t");
|
||||||
|
std::string_view first = std::string_view(hc.content).substr(0, space);
|
||||||
|
|
||||||
|
if (first == "nolint")
|
||||||
|
{
|
||||||
|
std::string::size_type notspace = hc.content.find_first_not_of(" \t", space);
|
||||||
|
|
||||||
|
if (space == std::string::npos || notspace == std::string::npos)
|
||||||
|
{
|
||||||
|
// disables all lints
|
||||||
|
}
|
||||||
|
else if (LintWarning::parseName(hc.content.c_str() + notspace) == LintWarning::Code_Unknown)
|
||||||
|
{
|
||||||
|
const char* rule = hc.content.c_str() + notspace;
|
||||||
|
|
||||||
|
// skip Unknown
|
||||||
|
if (const char* suggestion = fuzzyMatch(rule, kWarningNames + 1, LintWarning::Code__Count - 1))
|
||||||
|
emitWarning(context, LintWarning::Code_CommentDirective, hc.location,
|
||||||
|
"nolint directive refers to unknown lint rule '%s'; did you mean '%s'?", rule, suggestion);
|
||||||
|
else
|
||||||
|
emitWarning(
|
||||||
|
context, LintWarning::Code_CommentDirective, hc.location, "nolint directive refers to unknown lint rule '%s'", rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (first == "nocheck" || first == "nonstrict" || first == "strict")
|
||||||
|
{
|
||||||
|
if (space != std::string::npos)
|
||||||
|
emitWarning(context, LintWarning::Code_CommentDirective, hc.location,
|
||||||
|
"Comment directive with the type checking mode has extra symbols at the end of the line");
|
||||||
|
else if (seenMode)
|
||||||
|
emitWarning(context, LintWarning::Code_CommentDirective, hc.location,
|
||||||
|
"Comment directive with the type checking mode has already been used");
|
||||||
|
else
|
||||||
|
seenMode = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static const char* kHotComments[] = {
|
||||||
|
"nolint",
|
||||||
|
"nocheck",
|
||||||
|
"nonstrict",
|
||||||
|
"strict",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (const char* suggestion = fuzzyMatch(first, kHotComments, std::size(kHotComments)))
|
||||||
|
emitWarning(context, LintWarning::Code_CommentDirective, hc.location, "Unknown comment directive '%.*s'; did you mean '%s'?",
|
||||||
|
int(first.size()), first.data(), suggestion);
|
||||||
|
else
|
||||||
|
emitWarning(context, LintWarning::Code_CommentDirective, hc.location, "Unknown comment directive '%.*s'", int(first.size()),
|
||||||
|
first.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LintOptions::setDefaults()
|
void LintOptions::setDefaults()
|
||||||
{
|
{
|
||||||
// By default, we enable all warnings
|
// By default, we enable all warnings
|
||||||
warningMask = ~0ull;
|
warningMask = ~0ull;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const ScopePtr& env, const Module* module, const LintOptions& options)
|
std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const ScopePtr& env, const Module* module,
|
||||||
|
const std::vector<HotComment>& hotcomments, const LintOptions& options)
|
||||||
{
|
{
|
||||||
LintContext context;
|
LintContext context;
|
||||||
|
|
||||||
|
@ -2609,6 +2707,9 @@ std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const Sc
|
||||||
if (context.warningEnabled(LintWarning::Code_MisleadingAndOr))
|
if (context.warningEnabled(LintWarning::Code_MisleadingAndOr))
|
||||||
LintMisleadingAndOr::process(context);
|
LintMisleadingAndOr::process(context);
|
||||||
|
|
||||||
|
if (context.warningEnabled(LintWarning::Code_CommentDirective))
|
||||||
|
lintComments(context, hotcomments);
|
||||||
|
|
||||||
std::sort(context.result.begin(), context.result.end(), WarningComparator());
|
std::sort(context.result.begin(), context.result.end(), WarningComparator());
|
||||||
|
|
||||||
return context.result;
|
return context.result;
|
||||||
|
@ -2630,23 +2731,30 @@ LintWarning::Code LintWarning::parseName(const char* name)
|
||||||
return Code_Unknown;
|
return Code_Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t LintWarning::parseMask(const std::vector<std::string>& hotcomments)
|
uint64_t LintWarning::parseMask(const std::vector<HotComment>& hotcomments)
|
||||||
{
|
{
|
||||||
uint64_t result = 0;
|
uint64_t result = 0;
|
||||||
|
|
||||||
for (const std::string& hc : hotcomments)
|
for (const HotComment& hc : hotcomments)
|
||||||
{
|
{
|
||||||
if (hc.compare(0, 6, "nolint") != 0)
|
if (!hc.header)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string::size_type name = hc.find_first_not_of(" \t", 6);
|
if (hc.content.compare(0, 6, "nolint") != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string::size_type name = hc.content.find_first_not_of(" \t", 6);
|
||||||
|
|
||||||
// --!nolint disables everything
|
// --!nolint disables everything
|
||||||
if (name == std::string::npos)
|
if (name == std::string::npos)
|
||||||
return ~0ull;
|
return ~0ull;
|
||||||
|
|
||||||
|
// --!nolint needs to be followed by a whitespace character
|
||||||
|
if (name == 6)
|
||||||
|
continue;
|
||||||
|
|
||||||
// --!nolint name disables the specific lint
|
// --!nolint name disables the specific lint
|
||||||
LintWarning::Code code = LintWarning::parseName(hc.c_str() + name);
|
LintWarning::Code code = LintWarning::parseName(hc.content.c_str() + name);
|
||||||
|
|
||||||
if (code != LintWarning::Code_Unknown)
|
if (code != LintWarning::Code_Unknown)
|
||||||
result |= 1ull << int(code);
|
result |= 1ull << int(code);
|
||||||
|
|
|
@ -9,14 +9,12 @@ namespace Luau
|
||||||
|
|
||||||
struct Quantifier
|
struct Quantifier
|
||||||
{
|
{
|
||||||
ModulePtr module;
|
|
||||||
TypeLevel level;
|
TypeLevel level;
|
||||||
std::vector<TypeId> generics;
|
std::vector<TypeId> generics;
|
||||||
std::vector<TypePackId> genericPacks;
|
std::vector<TypePackId> genericPacks;
|
||||||
|
|
||||||
Quantifier(ModulePtr module, TypeLevel level)
|
Quantifier(TypeLevel level)
|
||||||
: module(module)
|
: level(level)
|
||||||
, level(level)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,9 +74,9 @@ struct Quantifier
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void quantify(ModulePtr module, TypeId ty, TypeLevel level)
|
void quantify(TypeId ty, TypeLevel level)
|
||||||
{
|
{
|
||||||
Quantifier q{std::move(module), level};
|
Quantifier q{level};
|
||||||
DenseHashSet<void*> seen{nullptr};
|
DenseHashSet<void*> seen{nullptr};
|
||||||
visitTypeVarOnce(ty, q, seen);
|
visitTypeVarOnce(ty, q, seen);
|
||||||
|
|
||||||
|
|
|
@ -226,27 +226,11 @@ TarjanResult Tarjan::loop()
|
||||||
return TarjanResult::Ok;
|
return TarjanResult::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tarjan::clear()
|
|
||||||
{
|
|
||||||
typeToIndex.clear();
|
|
||||||
indexToType.clear();
|
|
||||||
packToIndex.clear();
|
|
||||||
indexToPack.clear();
|
|
||||||
lowlink.clear();
|
|
||||||
stack.clear();
|
|
||||||
onStack.clear();
|
|
||||||
|
|
||||||
edgesTy.clear();
|
|
||||||
edgesTp.clear();
|
|
||||||
worklist.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
TarjanResult Tarjan::visitRoot(TypeId ty)
|
TarjanResult Tarjan::visitRoot(TypeId ty)
|
||||||
{
|
{
|
||||||
childCount = 0;
|
childCount = 0;
|
||||||
ty = log->follow(ty);
|
ty = log->follow(ty);
|
||||||
|
|
||||||
clear();
|
|
||||||
auto [index, fresh] = indexify(ty);
|
auto [index, fresh] = indexify(ty);
|
||||||
worklist.push_back({index, -1, -1});
|
worklist.push_back({index, -1, -1});
|
||||||
return loop();
|
return loop();
|
||||||
|
@ -257,7 +241,6 @@ TarjanResult Tarjan::visitRoot(TypePackId tp)
|
||||||
childCount = 0;
|
childCount = 0;
|
||||||
tp = log->follow(tp);
|
tp = log->follow(tp);
|
||||||
|
|
||||||
clear();
|
|
||||||
auto [index, fresh] = indexify(tp);
|
auto [index, fresh] = indexify(tp);
|
||||||
worklist.push_back({index, -1, -1});
|
worklist.push_back({index, -1, -1});
|
||||||
return loop();
|
return loop();
|
||||||
|
@ -314,21 +297,17 @@ void FindDirty::visitSCC(int index)
|
||||||
|
|
||||||
TarjanResult FindDirty::findDirty(TypeId ty)
|
TarjanResult FindDirty::findDirty(TypeId ty)
|
||||||
{
|
{
|
||||||
dirty.clear();
|
|
||||||
return visitRoot(ty);
|
return visitRoot(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
TarjanResult FindDirty::findDirty(TypePackId tp)
|
TarjanResult FindDirty::findDirty(TypePackId tp)
|
||||||
{
|
{
|
||||||
dirty.clear();
|
|
||||||
return visitRoot(tp);
|
return visitRoot(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> Substitution::substitute(TypeId ty)
|
std::optional<TypeId> Substitution::substitute(TypeId ty)
|
||||||
{
|
{
|
||||||
ty = log->follow(ty);
|
ty = log->follow(ty);
|
||||||
newTypes.clear();
|
|
||||||
newPacks.clear();
|
|
||||||
|
|
||||||
auto result = findDirty(ty);
|
auto result = findDirty(ty);
|
||||||
if (result != TarjanResult::Ok)
|
if (result != TarjanResult::Ok)
|
||||||
|
@ -347,8 +326,6 @@ std::optional<TypeId> Substitution::substitute(TypeId ty)
|
||||||
std::optional<TypePackId> Substitution::substitute(TypePackId tp)
|
std::optional<TypePackId> Substitution::substitute(TypePackId tp)
|
||||||
{
|
{
|
||||||
tp = log->follow(tp);
|
tp = log->follow(tp);
|
||||||
newTypes.clear();
|
|
||||||
newPacks.clear();
|
|
||||||
|
|
||||||
auto result = findDirty(tp);
|
auto result = findDirty(tp);
|
||||||
if (result != TarjanResult::Ok)
|
if (result != TarjanResult::Ok)
|
||||||
|
|
|
@ -26,9 +26,10 @@
|
||||||
* 3. Cyclic dependencies can be resolved by picking an arbitrary statement to check first.
|
* 3. Cyclic dependencies can be resolved by picking an arbitrary statement to check first.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Luau/Parser.h"
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
#include "Luau/StringUtils.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
|
@ -933,12 +933,12 @@ struct Printer
|
||||||
|
|
||||||
writer.symbol(")");
|
writer.symbol(")");
|
||||||
|
|
||||||
if (writeTypes && func.hasReturnAnnotation)
|
if (writeTypes && func.returnAnnotation)
|
||||||
{
|
{
|
||||||
writer.symbol(":");
|
writer.symbol(":");
|
||||||
writer.space();
|
writer.space();
|
||||||
|
|
||||||
visualizeTypeList(func.returnAnnotation, false);
|
visualizeTypeList(*func.returnAnnotation, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
visualizeBlock(*func.body);
|
visualizeBlock(*func.body);
|
||||||
|
@ -989,9 +989,9 @@ struct Printer
|
||||||
advance(typeAnnotation.location.begin);
|
advance(typeAnnotation.location.begin);
|
||||||
if (const auto& a = typeAnnotation.as<AstTypeReference>())
|
if (const auto& a = typeAnnotation.as<AstTypeReference>())
|
||||||
{
|
{
|
||||||
if (a->hasPrefix)
|
if (a->prefix)
|
||||||
{
|
{
|
||||||
writer.write(a->prefix.value);
|
writer.write(a->prefix->value);
|
||||||
writer.symbol(".");
|
writer.symbol(".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include "Luau/Error.h"
|
#include "Luau/Error.h"
|
||||||
#include "Luau/Module.h"
|
#include "Luau/Module.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/RecursionCounter.h"
|
#include "Luau/RecursionCounter.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
|
@ -476,12 +475,11 @@ public:
|
||||||
visitLocal(arg);
|
visitLocal(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fn->hasReturnAnnotation)
|
if (!fn->returnAnnotation)
|
||||||
{
|
{
|
||||||
if (auto result = getScope(fn->body->location))
|
if (auto result = getScope(fn->body->location))
|
||||||
{
|
{
|
||||||
TypePackId ret = result->returnType;
|
TypePackId ret = result->returnType;
|
||||||
fn->hasReturnAnnotation = true;
|
|
||||||
|
|
||||||
AstTypePack* variadicAnnotation = nullptr;
|
AstTypePack* variadicAnnotation = nullptr;
|
||||||
const auto& [v, tail] = flatten(ret);
|
const auto& [v, tail] = flatten(ret);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/Quantify.h"
|
#include "Luau/Quantify.h"
|
||||||
#include "Luau/RecursionCounter.h"
|
#include "Luau/RecursionCounter.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
|
@ -24,16 +23,12 @@ LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000)
|
||||||
LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 500)
|
LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 500)
|
||||||
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false)
|
LUAU_FASTFLAGVARIABLE(LuauEqConstraint, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGroupExpectedType, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false.
|
LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as false.
|
||||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false)
|
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIfElseBranchTypeUnion, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauIfElseExpectedType2, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauImmutableTypes, false)
|
LUAU_FASTFLAGVARIABLE(LuauImmutableTypes, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauLengthOnCompositeType, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauNoSealedTypeMod, false)
|
LUAU_FASTFLAGVARIABLE(LuauNoSealedTypeMod, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
|
LUAU_FASTFLAGVARIABLE(LuauQuantifyInPlace2, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSealExports, false)
|
LUAU_FASTFLAGVARIABLE(LuauSealExports, false)
|
||||||
|
@ -43,7 +38,6 @@ LUAU_FASTFLAGVARIABLE(LuauTypeAliasDefaults, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
|
LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
|
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
|
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauPerModuleUnificationCache, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauProperTypeLevels, false)
|
LUAU_FASTFLAGVARIABLE(LuauProperTypeLevels, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAscribeCorrectLevelToInferredProperitesOfFreeTables, false)
|
LUAU_FASTFLAGVARIABLE(LuauAscribeCorrectLevelToInferredProperitesOfFreeTables, false)
|
||||||
LUAU_FASTFLAG(LuauUnionTagMatchFix)
|
LUAU_FASTFLAG(LuauUnionTagMatchFix)
|
||||||
|
@ -293,13 +287,10 @@ ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optiona
|
||||||
GenericError{"Free types leaked into this module's public interface. This is an internal Luau error; please report it."}});
|
GenericError{"Free types leaked into this module's public interface. This is an internal Luau error; please report it."}});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FFlag::LuauPerModuleUnificationCache)
|
// Clear unifier cache since it's keyed off internal types that get deallocated
|
||||||
{
|
// This avoids fake cross-module cache hits and keeps cache size at bay when typechecking large module graphs.
|
||||||
// Clear unifier cache since it's keyed off internal types that get deallocated
|
unifierState.cachedUnify.clear();
|
||||||
// This avoids fake cross-module cache hits and keeps cache size at bay when typechecking large module graphs.
|
unifierState.skipCacheForType.clear();
|
||||||
unifierState.cachedUnify.clear();
|
|
||||||
unifierState.skipCacheForType.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FFlag::LuauTwoPassAliasDefinitionFix)
|
if (FFlag::LuauTwoPassAliasDefinitionFix)
|
||||||
duplicateTypeAliases.clear();
|
duplicateTypeAliases.clear();
|
||||||
|
@ -509,7 +500,7 @@ LUAU_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std
|
||||||
{
|
{
|
||||||
if (const auto& typealias = stat->as<AstStatTypeAlias>())
|
if (const auto& typealias = stat->as<AstStatTypeAlias>())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauTwoPassAliasDefinitionFix && typealias->name == Parser::errorName)
|
if (FFlag::LuauTwoPassAliasDefinitionFix && typealias->name == kParseNameError)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto& bindings = typealias->exported ? scope->exportedTypeBindings : scope->privateTypeBindings;
|
auto& bindings = typealias->exported ? scope->exportedTypeBindings : scope->privateTypeBindings;
|
||||||
|
@ -1193,7 +1184,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||||
Name name = typealias.name.value;
|
Name name = typealias.name.value;
|
||||||
|
|
||||||
// If the alias is missing a name, we can't do anything with it. Ignore it.
|
// If the alias is missing a name, we can't do anything with it. Ignore it.
|
||||||
if (FFlag::LuauTwoPassAliasDefinitionFix && name == Parser::errorName)
|
if (FFlag::LuauTwoPassAliasDefinitionFix && name == kParseNameError)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::optional<TypeFun> binding;
|
std::optional<TypeFun> binding;
|
||||||
|
@ -1222,7 +1213,8 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias
|
||||||
if (FFlag::LuauProperTypeLevels)
|
if (FFlag::LuauProperTypeLevels)
|
||||||
aliasScope->level.subLevel = subLevel;
|
aliasScope->level.subLevel = subLevel;
|
||||||
|
|
||||||
auto [generics, genericPacks] = createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks, /* useCache = */ true);
|
auto [generics, genericPacks] =
|
||||||
|
createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks, /* useCache = */ true);
|
||||||
|
|
||||||
TypeId ty = freshType(aliasScope);
|
TypeId ty = freshType(aliasScope);
|
||||||
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
FreeTypeVar* ftv = getMutable<FreeTypeVar>(ty);
|
||||||
|
@ -1464,7 +1456,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExpr&
|
||||||
ExprResult<TypeId> result;
|
ExprResult<TypeId> result;
|
||||||
|
|
||||||
if (auto a = expr.as<AstExprGroup>())
|
if (auto a = expr.as<AstExprGroup>())
|
||||||
result = checkExpr(scope, *a->expr, FFlag::LuauGroupExpectedType ? expectedType : std::nullopt);
|
result = checkExpr(scope, *a->expr, expectedType);
|
||||||
else if (expr.is<AstExprConstantNil>())
|
else if (expr.is<AstExprConstantNil>())
|
||||||
result = {nilType};
|
result = {nilType};
|
||||||
else if (const AstExprConstantBool* bexpr = expr.as<AstExprConstantBool>())
|
else if (const AstExprConstantBool* bexpr = expr.as<AstExprConstantBool>())
|
||||||
|
@ -1508,7 +1500,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExpr&
|
||||||
else if (auto a = expr.as<AstExprError>())
|
else if (auto a = expr.as<AstExprError>())
|
||||||
result = checkExpr(scope, *a);
|
result = checkExpr(scope, *a);
|
||||||
else if (auto a = expr.as<AstExprIfElse>())
|
else if (auto a = expr.as<AstExprIfElse>())
|
||||||
result = checkExpr(scope, *a, FFlag::LuauIfElseExpectedType2 ? expectedType : std::nullopt);
|
result = checkExpr(scope, *a, expectedType);
|
||||||
else
|
else
|
||||||
ice("Unhandled AstExpr?");
|
ice("Unhandled AstExpr?");
|
||||||
|
|
||||||
|
@ -2093,6 +2085,7 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprUn
|
||||||
return {numberType};
|
return {numberType};
|
||||||
}
|
}
|
||||||
case AstExprUnary::Len:
|
case AstExprUnary::Len:
|
||||||
|
{
|
||||||
tablify(operandType);
|
tablify(operandType);
|
||||||
|
|
||||||
operandType = stripFromNilAndReport(operandType, expr.location);
|
operandType = stripFromNilAndReport(operandType, expr.location);
|
||||||
|
@ -2100,30 +2093,13 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprUn
|
||||||
if (get<ErrorTypeVar>(operandType))
|
if (get<ErrorTypeVar>(operandType))
|
||||||
return {errorRecoveryType(scope)};
|
return {errorRecoveryType(scope)};
|
||||||
|
|
||||||
if (FFlag::LuauLengthOnCompositeType)
|
DenseHashSet<TypeId> seen{nullptr};
|
||||||
{
|
|
||||||
DenseHashSet<TypeId> seen{nullptr};
|
|
||||||
|
|
||||||
if (!hasLength(operandType, seen, &recursionCount))
|
if (!hasLength(operandType, seen, &recursionCount))
|
||||||
reportError(TypeError{expr.location, NotATable{operandType}});
|
reportError(TypeError{expr.location, NotATable{operandType}});
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (get<AnyTypeVar>(operandType))
|
|
||||||
return {numberType}; // Not strictly correct: metatables permit overriding this
|
|
||||||
|
|
||||||
if (auto p = get<PrimitiveTypeVar>(operandType))
|
|
||||||
{
|
|
||||||
if (p->type == PrimitiveTypeVar::String)
|
|
||||||
return {numberType};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!getTableType(operandType))
|
|
||||||
reportError(TypeError{expr.location, NotATable{operandType}});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {numberType};
|
return {numberType};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
ice("Unknown AstExprUnary " + std::to_string(int(expr.op)));
|
ice("Unknown AstExprUnary " + std::to_string(int(expr.op)));
|
||||||
}
|
}
|
||||||
|
@ -2618,22 +2594,11 @@ ExprResult<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExprIf
|
||||||
resolve(result.predicates, falseScope, false);
|
resolve(result.predicates, falseScope, false);
|
||||||
ExprResult<TypeId> falseType = checkExpr(falseScope, *expr.falseExpr, expectedType);
|
ExprResult<TypeId> falseType = checkExpr(falseScope, *expr.falseExpr, expectedType);
|
||||||
|
|
||||||
if (FFlag::LuauIfElseBranchTypeUnion)
|
if (falseType.type == trueType.type)
|
||||||
{
|
|
||||||
if (falseType.type == trueType.type)
|
|
||||||
return {trueType.type};
|
|
||||||
|
|
||||||
std::vector<TypeId> types = reduceUnion({trueType.type, falseType.type});
|
|
||||||
return {types.size() == 1 ? types[0] : addType(UnionTypeVar{std::move(types)})};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unify(falseType.type, trueType.type, expr.location);
|
|
||||||
|
|
||||||
// TODO: normalize(UnionTypeVar{{trueType, falseType}})
|
|
||||||
// For now both trueType and falseType must be the same type.
|
|
||||||
return {trueType.type};
|
return {trueType.type};
|
||||||
}
|
|
||||||
|
std::vector<TypeId> types = reduceUnion({trueType.type, falseType.type});
|
||||||
|
return {types.size() == 1 ? types[0] : addType(UnionTypeVar{std::move(types)})};
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId TypeChecker::checkLValue(const ScopePtr& scope, const AstExpr& expr)
|
TypeId TypeChecker::checkLValue(const ScopePtr& scope, const AstExpr& expr)
|
||||||
|
@ -2986,8 +2951,8 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(
|
||||||
auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
|
auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks);
|
||||||
|
|
||||||
TypePackId retPack;
|
TypePackId retPack;
|
||||||
if (expr.hasReturnAnnotation)
|
if (expr.returnAnnotation)
|
||||||
retPack = resolveTypePack(funScope, expr.returnAnnotation);
|
retPack = resolveTypePack(funScope, *expr.returnAnnotation);
|
||||||
else if (isNonstrictMode())
|
else if (isNonstrictMode())
|
||||||
retPack = anyTypePack;
|
retPack = anyTypePack;
|
||||||
else if (expectedFunctionType)
|
else if (expectedFunctionType)
|
||||||
|
@ -3181,7 +3146,7 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE
|
||||||
// If we're in nonstrict mode we want to only report this missing return
|
// If we're in nonstrict mode we want to only report this missing return
|
||||||
// statement if there are type annotations on the function. In strict mode
|
// statement if there are type annotations on the function. In strict mode
|
||||||
// we report it regardless.
|
// we report it regardless.
|
||||||
if (!isNonstrictMode() || function.hasReturnAnnotation)
|
if (!isNonstrictMode() || function.returnAnnotation)
|
||||||
{
|
{
|
||||||
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retType});
|
reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retType});
|
||||||
}
|
}
|
||||||
|
@ -4403,11 +4368,7 @@ TypeId Instantiation::clean(TypeId ty)
|
||||||
|
|
||||||
// Annoyingly, we have to do this even if there are no generics,
|
// Annoyingly, we have to do this even if there are no generics,
|
||||||
// to replace any generic tables.
|
// to replace any generic tables.
|
||||||
replaceGenerics.log = log;
|
ReplaceGenerics replaceGenerics{log, arena, level, ftv->generics, ftv->genericPacks};
|
||||||
replaceGenerics.level = level;
|
|
||||||
replaceGenerics.currentModule = currentModule;
|
|
||||||
replaceGenerics.generics.assign(ftv->generics.begin(), ftv->generics.end());
|
|
||||||
replaceGenerics.genericPacks.assign(ftv->genericPacks.begin(), ftv->genericPacks.end());
|
|
||||||
|
|
||||||
// TODO: What to do if this returns nullopt?
|
// TODO: What to do if this returns nullopt?
|
||||||
// We don't have access to the error-reporting machinery
|
// We don't have access to the error-reporting machinery
|
||||||
|
@ -4573,16 +4534,11 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
|
||||||
|
|
||||||
if (FFlag::LuauQuantifyInPlace2)
|
if (FFlag::LuauQuantifyInPlace2)
|
||||||
{
|
{
|
||||||
Luau::quantify(currentModule, ty, scope->level);
|
Luau::quantify(ty, scope->level);
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
quantification.log = TxnLog::empty();
|
Quantification quantification{¤tModule->internalTypes, scope->level};
|
||||||
quantification.level = scope->level;
|
|
||||||
quantification.generics.clear();
|
|
||||||
quantification.genericPacks.clear();
|
|
||||||
quantification.currentModule = currentModule;
|
|
||||||
|
|
||||||
std::optional<TypeId> qty = quantification.substitute(ty);
|
std::optional<TypeId> qty = quantification.substitute(ty);
|
||||||
|
|
||||||
if (!qty.has_value())
|
if (!qty.has_value())
|
||||||
|
@ -4596,18 +4552,14 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
|
||||||
|
|
||||||
FunctionTypeVar* qftv = getMutable<FunctionTypeVar>(*qty);
|
FunctionTypeVar* qftv = getMutable<FunctionTypeVar>(*qty);
|
||||||
LUAU_ASSERT(qftv);
|
LUAU_ASSERT(qftv);
|
||||||
qftv->generics = quantification.generics;
|
qftv->generics = std::move(quantification.generics);
|
||||||
qftv->genericPacks = quantification.genericPacks;
|
qftv->genericPacks = std::move(quantification.genericPacks);
|
||||||
return *qty;
|
return *qty;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log)
|
TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(log != nullptr);
|
Instantiation instantiation{FFlag::LuauUseCommittingTxnLog ? log : TxnLog::empty(), ¤tModule->internalTypes, scope->level};
|
||||||
|
|
||||||
instantiation.log = FFlag::LuauUseCommittingTxnLog ? log : TxnLog::empty();
|
|
||||||
instantiation.level = scope->level;
|
|
||||||
instantiation.currentModule = currentModule;
|
|
||||||
std::optional<TypeId> instantiated = instantiation.substitute(ty);
|
std::optional<TypeId> instantiated = instantiation.substitute(ty);
|
||||||
if (instantiated.has_value())
|
if (instantiated.has_value())
|
||||||
return *instantiated;
|
return *instantiated;
|
||||||
|
@ -4620,10 +4572,7 @@ TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location locat
|
||||||
|
|
||||||
TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location)
|
TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location)
|
||||||
{
|
{
|
||||||
anyification.log = TxnLog::empty();
|
Anyification anyification{¤tModule->internalTypes, anyType, anyTypePack};
|
||||||
anyification.anyType = anyType;
|
|
||||||
anyification.anyTypePack = anyTypePack;
|
|
||||||
anyification.currentModule = currentModule;
|
|
||||||
std::optional<TypeId> any = anyification.substitute(ty);
|
std::optional<TypeId> any = anyification.substitute(ty);
|
||||||
if (any.has_value())
|
if (any.has_value())
|
||||||
return *any;
|
return *any;
|
||||||
|
@ -4636,10 +4585,7 @@ TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location)
|
||||||
|
|
||||||
TypePackId TypeChecker::anyify(const ScopePtr& scope, TypePackId ty, Location location)
|
TypePackId TypeChecker::anyify(const ScopePtr& scope, TypePackId ty, Location location)
|
||||||
{
|
{
|
||||||
anyification.log = TxnLog::empty();
|
Anyification anyification{¤tModule->internalTypes, anyType, anyTypePack};
|
||||||
anyification.anyType = anyType;
|
|
||||||
anyification.anyTypePack = anyTypePack;
|
|
||||||
anyification.currentModule = currentModule;
|
|
||||||
std::optional<TypePackId> any = anyification.substitute(ty);
|
std::optional<TypePackId> any = anyification.substitute(ty);
|
||||||
if (any.has_value())
|
if (any.has_value())
|
||||||
return *any;
|
return *any;
|
||||||
|
@ -4823,7 +4769,8 @@ TypePackId TypeChecker::errorRecoveryTypePack(TypePackId guess)
|
||||||
return getSingletonTypes().errorRecoveryTypePack(guess);
|
return getSingletonTypes().errorRecoveryTypePack(guess);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeIdPredicate TypeChecker::mkTruthyPredicate(bool sense) {
|
TypeIdPredicate TypeChecker::mkTruthyPredicate(bool sense)
|
||||||
|
{
|
||||||
return [this, sense](TypeId ty) -> std::optional<TypeId> {
|
return [this, sense](TypeId ty) -> std::optional<TypeId> {
|
||||||
// any/error/free gets a special pass unconditionally because they can't be decided.
|
// any/error/free gets a special pass unconditionally because they can't be decided.
|
||||||
if (get<AnyTypeVar>(ty) || get<ErrorTypeVar>(ty) || get<FreeTypeVar>(ty))
|
if (get<AnyTypeVar>(ty) || get<ErrorTypeVar>(ty) || get<FreeTypeVar>(ty))
|
||||||
|
@ -4904,8 +4851,8 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
||||||
if (const auto& lit = annotation.as<AstTypeReference>())
|
if (const auto& lit = annotation.as<AstTypeReference>())
|
||||||
{
|
{
|
||||||
std::optional<TypeFun> tf;
|
std::optional<TypeFun> tf;
|
||||||
if (lit->hasPrefix)
|
if (lit->prefix)
|
||||||
tf = scope->lookupImportedType(lit->prefix.value, lit->name.value);
|
tf = scope->lookupImportedType(lit->prefix->value, lit->name.value);
|
||||||
|
|
||||||
else if (FFlag::DebugLuauMagicTypes && lit->name == "_luau_ice")
|
else if (FFlag::DebugLuauMagicTypes && lit->name == "_luau_ice")
|
||||||
ice("_luau_ice encountered", lit->location);
|
ice("_luau_ice encountered", lit->location);
|
||||||
|
@ -4932,12 +4879,12 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
||||||
|
|
||||||
if (!tf)
|
if (!tf)
|
||||||
{
|
{
|
||||||
if (lit->name == Parser::errorName)
|
if (lit->name == kParseNameError)
|
||||||
return errorRecoveryType(scope);
|
return errorRecoveryType(scope);
|
||||||
|
|
||||||
std::string typeName;
|
std::string typeName;
|
||||||
if (lit->hasPrefix)
|
if (lit->prefix)
|
||||||
typeName = std::string(lit->prefix.value) + ".";
|
typeName = std::string(lit->prefix->value) + ".";
|
||||||
typeName += lit->name.value;
|
typeName += lit->name.value;
|
||||||
|
|
||||||
if (scope->lookupPack(typeName))
|
if (scope->lookupPack(typeName))
|
||||||
|
@ -5038,12 +4985,7 @@ TypeId TypeChecker::resolveType(const ScopePtr& scope, const AstType& annotation
|
||||||
if (notEnoughParameters && hasDefaultParameters)
|
if (notEnoughParameters && hasDefaultParameters)
|
||||||
{
|
{
|
||||||
// 'applyTypeFunction' is used to substitute default types that reference previous generic types
|
// 'applyTypeFunction' is used to substitute default types that reference previous generic types
|
||||||
applyTypeFunction.log = TxnLog::empty();
|
ApplyTypeFunction applyTypeFunction{¤tModule->internalTypes, scope->level};
|
||||||
applyTypeFunction.typeArguments.clear();
|
|
||||||
applyTypeFunction.typePackArguments.clear();
|
|
||||||
applyTypeFunction.currentModule = currentModule;
|
|
||||||
applyTypeFunction.level = scope->level;
|
|
||||||
applyTypeFunction.encounteredForwardedType = false;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < typesProvided; ++i)
|
for (size_t i = 0; i < typesProvided; ++i)
|
||||||
applyTypeFunction.typeArguments[tf->typeParams[i].ty] = typeParams[i];
|
applyTypeFunction.typeArguments[tf->typeParams[i].ty] = typeParams[i];
|
||||||
|
@ -5362,18 +5304,14 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf,
|
||||||
if (tf.typeParams.empty() && tf.typePackParams.empty())
|
if (tf.typeParams.empty() && tf.typePackParams.empty())
|
||||||
return tf.type;
|
return tf.type;
|
||||||
|
|
||||||
applyTypeFunction.typeArguments.clear();
|
ApplyTypeFunction applyTypeFunction{¤tModule->internalTypes, scope->level};
|
||||||
|
|
||||||
for (size_t i = 0; i < tf.typeParams.size(); ++i)
|
for (size_t i = 0; i < tf.typeParams.size(); ++i)
|
||||||
applyTypeFunction.typeArguments[tf.typeParams[i].ty] = typeParams[i];
|
applyTypeFunction.typeArguments[tf.typeParams[i].ty] = typeParams[i];
|
||||||
|
|
||||||
applyTypeFunction.typePackArguments.clear();
|
|
||||||
for (size_t i = 0; i < tf.typePackParams.size(); ++i)
|
for (size_t i = 0; i < tf.typePackParams.size(); ++i)
|
||||||
applyTypeFunction.typePackArguments[tf.typePackParams[i].tp] = typePackParams[i];
|
applyTypeFunction.typePackArguments[tf.typePackParams[i].tp] = typePackParams[i];
|
||||||
|
|
||||||
applyTypeFunction.log = TxnLog::empty();
|
|
||||||
applyTypeFunction.currentModule = currentModule;
|
|
||||||
applyTypeFunction.level = scope->level;
|
|
||||||
applyTypeFunction.encounteredForwardedType = false;
|
|
||||||
std::optional<TypeId> maybeInstantiated = applyTypeFunction.substitute(tf.type);
|
std::optional<TypeId> maybeInstantiated = applyTypeFunction.substitute(tf.type);
|
||||||
if (!maybeInstantiated.has_value())
|
if (!maybeInstantiated.has_value())
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTerminateCyclicMetatableIndexLookup, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -48,9 +50,19 @@ std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, const Sc
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> mtIndex = findMetatableEntry(errors, globalScope, ty, "__index", location);
|
std::optional<TypeId> mtIndex = findMetatableEntry(errors, globalScope, ty, "__index", location);
|
||||||
|
int count = 0;
|
||||||
while (mtIndex)
|
while (mtIndex)
|
||||||
{
|
{
|
||||||
TypeId index = follow(*mtIndex);
|
TypeId index = follow(*mtIndex);
|
||||||
|
|
||||||
|
if (FFlag::LuauTerminateCyclicMetatableIndexLookup)
|
||||||
|
{
|
||||||
|
if (count >= 100)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
if (const auto& itt = getTableType(index))
|
if (const auto& itt = getTableType(index))
|
||||||
{
|
{
|
||||||
const auto& fit = itt->props.find(name);
|
const auto& fit = itt->props.find(name);
|
||||||
|
|
|
@ -23,8 +23,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
|
||||||
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||||
LUAU_FASTFLAG(LuauLengthOnCompositeType)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauMetatableAreEqualRecursion, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauRefactorTypeVarQuestions, false)
|
LUAU_FASTFLAGVARIABLE(LuauRefactorTypeVarQuestions, false)
|
||||||
LUAU_FASTFLAG(LuauErrorRecoveryType)
|
LUAU_FASTFLAG(LuauErrorRecoveryType)
|
||||||
LUAU_FASTFLAG(LuauUnionTagMatchFix)
|
LUAU_FASTFLAG(LuauUnionTagMatchFix)
|
||||||
|
@ -385,8 +383,6 @@ bool maybeSingleton(TypeId ty)
|
||||||
|
|
||||||
bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount)
|
bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauLengthOnCompositeType);
|
|
||||||
|
|
||||||
RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit);
|
RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit);
|
||||||
|
|
||||||
ty = follow(ty);
|
ty = follow(ty);
|
||||||
|
@ -555,7 +551,7 @@ bool areEqual(SeenSet& seen, const TableTypeVar& lhs, const TableTypeVar& rhs)
|
||||||
|
|
||||||
static bool areEqual(SeenSet& seen, const MetatableTypeVar& lhs, const MetatableTypeVar& rhs)
|
static bool areEqual(SeenSet& seen, const MetatableTypeVar& lhs, const MetatableTypeVar& rhs)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauMetatableAreEqualRecursion && areSeen(seen, &lhs, &rhs))
|
if (areSeen(seen, &lhs, &rhs))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return areEqual(seen, *lhs.table, *rhs.table) && areEqual(seen, *lhs.metatable, *rhs.metatable);
|
return areEqual(seen, *lhs.table, *rhs.table) && areEqual(seen, *lhs.metatable, *rhs.metatable);
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
const size_t kPageSize = 4096;
|
const size_t kPageSize = 4096;
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
LUAU_FASTINT(LuauTypeInferRecursionLimit);
|
||||||
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCommittingTxnLogFreeTpPromote, false)
|
|
||||||
LUAU_FASTFLAG(LuauImmutableTypes)
|
LUAU_FASTFLAG(LuauImmutableTypes)
|
||||||
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
LUAU_FASTFLAG(LuauUseCommittingTxnLog)
|
||||||
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 2000);
|
LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 2000);
|
||||||
|
@ -23,7 +22,6 @@ LUAU_FASTFLAGVARIABLE(LuauTableUnificationEarlyTest, false)
|
||||||
LUAU_FASTFLAG(LuauSingletonTypes)
|
LUAU_FASTFLAG(LuauSingletonTypes)
|
||||||
LUAU_FASTFLAG(LuauErrorRecoveryType);
|
LUAU_FASTFLAG(LuauErrorRecoveryType);
|
||||||
LUAU_FASTFLAG(LuauProperTypeLevels);
|
LUAU_FASTFLAG(LuauProperTypeLevels);
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnifyPackTails, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUnionTagMatchFix, false)
|
LUAU_FASTFLAGVARIABLE(LuauUnionTagMatchFix, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFollowWithCommittingTxnLogInAnyUnification, false)
|
LUAU_FASTFLAGVARIABLE(LuauFollowWithCommittingTxnLogInAnyUnification, false)
|
||||||
|
|
||||||
|
@ -116,7 +114,7 @@ struct PromoteTypeLevels
|
||||||
{
|
{
|
||||||
// Surprise, it's actually a BoundTypePack that hasn't been committed yet.
|
// Surprise, it's actually a BoundTypePack that hasn't been committed yet.
|
||||||
// Calling getMutable on this will trigger an assertion.
|
// Calling getMutable on this will trigger an assertion.
|
||||||
if (FFlag::LuauCommittingTxnLogFreeTpPromote && FFlag::LuauUseCommittingTxnLog && !log.is<FreeTypePack>(tp))
|
if (FFlag::LuauUseCommittingTxnLog && !log.is<FreeTypePack>(tp))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
promote(tp, FFlag::LuauUseCommittingTxnLog ? log.getMutable<FreeTypePack>(tp) : getMutable<FreeTypePack>(tp));
|
promote(tp, FFlag::LuauUseCommittingTxnLog ? log.getMutable<FreeTypePack>(tp) : getMutable<FreeTypePack>(tp));
|
||||||
|
@ -1242,7 +1240,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
// If both are at the end, we're done
|
// If both are at the end, we're done
|
||||||
if (!superIter.good() && !subIter.good())
|
if (!superIter.good() && !subIter.good())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauUnifyPackTails && subTpv->tail && superTpv->tail)
|
if (subTpv->tail && superTpv->tail)
|
||||||
{
|
{
|
||||||
tryUnify_(*subTpv->tail, *superTpv->tail);
|
tryUnify_(*subTpv->tail, *superTpv->tail);
|
||||||
break;
|
break;
|
||||||
|
@ -1250,9 +1248,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
|
|
||||||
const bool lFreeTail = superTpv->tail && log.getMutable<FreeTypePack>(log.follow(*superTpv->tail)) != nullptr;
|
const bool lFreeTail = superTpv->tail && log.getMutable<FreeTypePack>(log.follow(*superTpv->tail)) != nullptr;
|
||||||
const bool rFreeTail = subTpv->tail && log.getMutable<FreeTypePack>(log.follow(*subTpv->tail)) != nullptr;
|
const bool rFreeTail = subTpv->tail && log.getMutable<FreeTypePack>(log.follow(*subTpv->tail)) != nullptr;
|
||||||
if (!FFlag::LuauUnifyPackTails && lFreeTail && rFreeTail)
|
if (lFreeTail)
|
||||||
tryUnify_(*subTpv->tail, *superTpv->tail);
|
|
||||||
else if (lFreeTail)
|
|
||||||
tryUnify_(emptyTp, *superTpv->tail);
|
tryUnify_(emptyTp, *superTpv->tail);
|
||||||
else if (rFreeTail)
|
else if (rFreeTail)
|
||||||
tryUnify_(emptyTp, *subTpv->tail);
|
tryUnify_(emptyTp, *subTpv->tail);
|
||||||
|
@ -1448,7 +1444,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
// If both are at the end, we're done
|
// If both are at the end, we're done
|
||||||
if (!superIter.good() && !subIter.good())
|
if (!superIter.good() && !subIter.good())
|
||||||
{
|
{
|
||||||
if (FFlag::LuauUnifyPackTails && subTpv->tail && superTpv->tail)
|
if (subTpv->tail && superTpv->tail)
|
||||||
{
|
{
|
||||||
tryUnify_(*subTpv->tail, *superTpv->tail);
|
tryUnify_(*subTpv->tail, *superTpv->tail);
|
||||||
break;
|
break;
|
||||||
|
@ -1456,9 +1452,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
|
||||||
|
|
||||||
const bool lFreeTail = superTpv->tail && get<FreeTypePack>(follow(*superTpv->tail)) != nullptr;
|
const bool lFreeTail = superTpv->tail && get<FreeTypePack>(follow(*superTpv->tail)) != nullptr;
|
||||||
const bool rFreeTail = subTpv->tail && get<FreeTypePack>(follow(*subTpv->tail)) != nullptr;
|
const bool rFreeTail = subTpv->tail && get<FreeTypePack>(follow(*subTpv->tail)) != nullptr;
|
||||||
if (!FFlag::LuauUnifyPackTails && lFreeTail && rFreeTail)
|
if (lFreeTail)
|
||||||
tryUnify_(*subTpv->tail, *superTpv->tail);
|
|
||||||
else if (lFreeTail)
|
|
||||||
tryUnify_(emptyTp, *superTpv->tail);
|
tryUnify_(emptyTp, *superTpv->tail);
|
||||||
else if (rFreeTail)
|
else if (rFreeTail)
|
||||||
tryUnify_(emptyTp, *subTpv->tail);
|
tryUnify_(emptyTp, *subTpv->tail);
|
||||||
|
|
|
@ -594,8 +594,7 @@ public:
|
||||||
AstArray<AstGenericTypePack> genericPacks;
|
AstArray<AstGenericTypePack> genericPacks;
|
||||||
AstLocal* self;
|
AstLocal* self;
|
||||||
AstArray<AstLocal*> args;
|
AstArray<AstLocal*> args;
|
||||||
bool hasReturnAnnotation;
|
std::optional<AstTypeList> returnAnnotation;
|
||||||
AstTypeList returnAnnotation;
|
|
||||||
bool vararg = false;
|
bool vararg = false;
|
||||||
Location varargLocation;
|
Location varargLocation;
|
||||||
AstTypePack* varargAnnotation;
|
AstTypePack* varargAnnotation;
|
||||||
|
@ -740,7 +739,7 @@ class AstStatIf : public AstStat
|
||||||
public:
|
public:
|
||||||
LUAU_RTTI(AstStatIf)
|
LUAU_RTTI(AstStatIf)
|
||||||
|
|
||||||
AstStatIf(const Location& location, AstExpr* condition, AstStatBlock* thenbody, AstStat* elsebody, bool hasThen, const Location& thenLocation,
|
AstStatIf(const Location& location, AstExpr* condition, AstStatBlock* thenbody, AstStat* elsebody, const std::optional<Location>& thenLocation,
|
||||||
const std::optional<Location>& elseLocation, bool hasEnd);
|
const std::optional<Location>& elseLocation, bool hasEnd);
|
||||||
|
|
||||||
void visit(AstVisitor* visitor) override;
|
void visit(AstVisitor* visitor) override;
|
||||||
|
@ -749,12 +748,10 @@ public:
|
||||||
AstStatBlock* thenbody;
|
AstStatBlock* thenbody;
|
||||||
AstStat* elsebody;
|
AstStat* elsebody;
|
||||||
|
|
||||||
bool hasThen = false;
|
std::optional<Location> thenLocation;
|
||||||
Location thenLocation;
|
|
||||||
|
|
||||||
// Active for 'elseif' as well
|
// Active for 'elseif' as well
|
||||||
bool hasElse = false;
|
std::optional<Location> elseLocation;
|
||||||
Location elseLocation;
|
|
||||||
|
|
||||||
bool hasEnd = false;
|
bool hasEnd = false;
|
||||||
};
|
};
|
||||||
|
@ -849,8 +846,7 @@ public:
|
||||||
AstArray<AstLocal*> vars;
|
AstArray<AstLocal*> vars;
|
||||||
AstArray<AstExpr*> values;
|
AstArray<AstExpr*> values;
|
||||||
|
|
||||||
bool hasEqualsSign = false;
|
std::optional<Location> equalsSignLocation;
|
||||||
Location equalsSignLocation;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AstStatFor : public AstStat
|
class AstStatFor : public AstStat
|
||||||
|
@ -1053,9 +1049,8 @@ public:
|
||||||
|
|
||||||
void visit(AstVisitor* visitor) override;
|
void visit(AstVisitor* visitor) override;
|
||||||
|
|
||||||
bool hasPrefix;
|
|
||||||
bool hasParameterList;
|
bool hasParameterList;
|
||||||
AstName prefix;
|
std::optional<AstName> prefix;
|
||||||
AstName name;
|
AstName name;
|
||||||
AstArray<AstTypeOrPack> parameters;
|
AstArray<AstTypeOrPack> parameters;
|
||||||
};
|
};
|
||||||
|
|
|
@ -233,4 +233,9 @@ private:
|
||||||
bool readNames;
|
bool readNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool isSpace(char ch)
|
||||||
|
{
|
||||||
|
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '\v' || ch == '\f';
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
69
Ast/include/Luau/ParseResult.h
Normal file
69
Ast/include/Luau/ParseResult.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
#include "Luau/Location.h"
|
||||||
|
#include "Luau/Lexer.h"
|
||||||
|
#include "Luau/StringUtils.h"
|
||||||
|
|
||||||
|
namespace Luau
|
||||||
|
{
|
||||||
|
|
||||||
|
class AstStatBlock;
|
||||||
|
|
||||||
|
class ParseError : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParseError(const Location& location, const std::string& message);
|
||||||
|
|
||||||
|
virtual const char* what() const throw();
|
||||||
|
|
||||||
|
const Location& getLocation() const;
|
||||||
|
const std::string& getMessage() const;
|
||||||
|
|
||||||
|
static LUAU_NORETURN void raise(const Location& location, const char* format, ...) LUAU_PRINTF_ATTR(2, 3);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Location location;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ParseErrors : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParseErrors(std::vector<ParseError> errors);
|
||||||
|
|
||||||
|
virtual const char* what() const throw();
|
||||||
|
|
||||||
|
const std::vector<ParseError>& getErrors() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<ParseError> errors;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HotComment
|
||||||
|
{
|
||||||
|
bool header;
|
||||||
|
Location location;
|
||||||
|
std::string content;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Comment
|
||||||
|
{
|
||||||
|
Lexeme::Type type; // Comment, BlockComment, or BrokenComment
|
||||||
|
Location location;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParseResult
|
||||||
|
{
|
||||||
|
AstStatBlock* root;
|
||||||
|
std::vector<HotComment> hotcomments;
|
||||||
|
std::vector<ParseError> errors;
|
||||||
|
|
||||||
|
std::vector<Comment> commentLocations;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr const char* kParseNameError = "%error-id%";
|
||||||
|
|
||||||
|
} // namespace Luau
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Luau/Ast.h"
|
#include "Luau/Ast.h"
|
||||||
#include "Luau/Lexer.h"
|
#include "Luau/Lexer.h"
|
||||||
#include "Luau/ParseOptions.h"
|
#include "Luau/ParseOptions.h"
|
||||||
|
#include "Luau/ParseResult.h"
|
||||||
#include "Luau/StringUtils.h"
|
#include "Luau/StringUtils.h"
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
@ -14,37 +15,6 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
class ParseError : public std::exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ParseError(const Location& location, const std::string& message);
|
|
||||||
|
|
||||||
virtual const char* what() const throw();
|
|
||||||
|
|
||||||
const Location& getLocation() const;
|
|
||||||
const std::string& getMessage() const;
|
|
||||||
|
|
||||||
static LUAU_NORETURN void raise(const Location& location, const char* format, ...) LUAU_PRINTF_ATTR(2, 3);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Location location;
|
|
||||||
std::string message;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ParseErrors : public std::exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ParseErrors(std::vector<ParseError> errors);
|
|
||||||
|
|
||||||
virtual const char* what() const throw();
|
|
||||||
|
|
||||||
const std::vector<ParseError>& getErrors() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<ParseError> errors;
|
|
||||||
std::string message;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class TempVector
|
class TempVector
|
||||||
{
|
{
|
||||||
|
@ -80,34 +50,17 @@ private:
|
||||||
size_t size_;
|
size_t size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Comment
|
|
||||||
{
|
|
||||||
Lexeme::Type type; // Comment, BlockComment, or BrokenComment
|
|
||||||
Location location;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ParseResult
|
|
||||||
{
|
|
||||||
AstStatBlock* root;
|
|
||||||
std::vector<std::string> hotcomments;
|
|
||||||
std::vector<ParseError> errors;
|
|
||||||
|
|
||||||
std::vector<Comment> commentLocations;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Parser
|
class Parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static ParseResult parse(
|
static ParseResult parse(
|
||||||
const char* buffer, std::size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options = ParseOptions());
|
const char* buffer, std::size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options = ParseOptions());
|
||||||
|
|
||||||
static constexpr const char* errorName = "%error-id%";
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Name;
|
struct Name;
|
||||||
struct Binding;
|
struct Binding;
|
||||||
|
|
||||||
Parser(const char* buffer, std::size_t bufferSize, AstNameTable& names, Allocator& allocator);
|
Parser(const char* buffer, std::size_t bufferSize, AstNameTable& names, Allocator& allocator, const ParseOptions& options);
|
||||||
|
|
||||||
bool blockFollow(const Lexeme& l);
|
bool blockFollow(const Lexeme& l);
|
||||||
|
|
||||||
|
@ -330,7 +283,7 @@ private:
|
||||||
AstTypeError* reportTypeAnnotationError(const Location& location, const AstArray<AstType*>& types, bool isMissing, const char* format, ...)
|
AstTypeError* reportTypeAnnotationError(const Location& location, const AstArray<AstType*>& types, bool isMissing, const char* format, ...)
|
||||||
LUAU_PRINTF_ATTR(5, 6);
|
LUAU_PRINTF_ATTR(5, 6);
|
||||||
|
|
||||||
const Lexeme& nextLexeme();
|
void nextLexeme();
|
||||||
|
|
||||||
struct Function
|
struct Function
|
||||||
{
|
{
|
||||||
|
@ -386,6 +339,9 @@ private:
|
||||||
Allocator& allocator;
|
Allocator& allocator;
|
||||||
|
|
||||||
std::vector<Comment> commentLocations;
|
std::vector<Comment> commentLocations;
|
||||||
|
std::vector<HotComment> hotcomments;
|
||||||
|
|
||||||
|
bool hotcommentHeader = true;
|
||||||
|
|
||||||
unsigned int recursionCounter;
|
unsigned int recursionCounter;
|
||||||
|
|
||||||
|
|
|
@ -167,8 +167,7 @@ AstExprFunction::AstExprFunction(const Location& location, const AstArray<AstGen
|
||||||
, genericPacks(genericPacks)
|
, genericPacks(genericPacks)
|
||||||
, self(self)
|
, self(self)
|
||||||
, args(args)
|
, args(args)
|
||||||
, hasReturnAnnotation(returnAnnotation.has_value())
|
, returnAnnotation(returnAnnotation)
|
||||||
, returnAnnotation()
|
|
||||||
, vararg(vararg.has_value())
|
, vararg(vararg.has_value())
|
||||||
, varargLocation(vararg.value_or(Location()))
|
, varargLocation(vararg.value_or(Location()))
|
||||||
, varargAnnotation(varargAnnotation)
|
, varargAnnotation(varargAnnotation)
|
||||||
|
@ -178,8 +177,6 @@ AstExprFunction::AstExprFunction(const Location& location, const AstArray<AstGen
|
||||||
, hasEnd(hasEnd)
|
, hasEnd(hasEnd)
|
||||||
, argLocation(argLocation)
|
, argLocation(argLocation)
|
||||||
{
|
{
|
||||||
if (returnAnnotation.has_value())
|
|
||||||
this->returnAnnotation = *returnAnnotation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstExprFunction::visit(AstVisitor* visitor)
|
void AstExprFunction::visit(AstVisitor* visitor)
|
||||||
|
@ -195,8 +192,8 @@ void AstExprFunction::visit(AstVisitor* visitor)
|
||||||
if (varargAnnotation)
|
if (varargAnnotation)
|
||||||
varargAnnotation->visit(visitor);
|
varargAnnotation->visit(visitor);
|
||||||
|
|
||||||
if (hasReturnAnnotation)
|
if (returnAnnotation)
|
||||||
visitTypeList(visitor, returnAnnotation);
|
visitTypeList(visitor, *returnAnnotation);
|
||||||
|
|
||||||
body->visit(visitor);
|
body->visit(visitor);
|
||||||
}
|
}
|
||||||
|
@ -375,21 +372,16 @@ void AstStatBlock::visit(AstVisitor* visitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AstStatIf::AstStatIf(const Location& location, AstExpr* condition, AstStatBlock* thenbody, AstStat* elsebody, bool hasThen,
|
AstStatIf::AstStatIf(const Location& location, AstExpr* condition, AstStatBlock* thenbody, AstStat* elsebody,
|
||||||
const Location& thenLocation, const std::optional<Location>& elseLocation, bool hasEnd)
|
const std::optional<Location>& thenLocation, const std::optional<Location>& elseLocation, bool hasEnd)
|
||||||
: AstStat(ClassIndex(), location)
|
: AstStat(ClassIndex(), location)
|
||||||
, condition(condition)
|
, condition(condition)
|
||||||
, thenbody(thenbody)
|
, thenbody(thenbody)
|
||||||
, elsebody(elsebody)
|
, elsebody(elsebody)
|
||||||
, hasThen(hasThen)
|
|
||||||
, thenLocation(thenLocation)
|
, thenLocation(thenLocation)
|
||||||
|
, elseLocation(elseLocation)
|
||||||
, hasEnd(hasEnd)
|
, hasEnd(hasEnd)
|
||||||
{
|
{
|
||||||
if (bool(elseLocation))
|
|
||||||
{
|
|
||||||
hasElse = true;
|
|
||||||
this->elseLocation = *elseLocation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstStatIf::visit(AstVisitor* visitor)
|
void AstStatIf::visit(AstVisitor* visitor)
|
||||||
|
@ -492,12 +484,8 @@ AstStatLocal::AstStatLocal(
|
||||||
: AstStat(ClassIndex(), location)
|
: AstStat(ClassIndex(), location)
|
||||||
, vars(vars)
|
, vars(vars)
|
||||||
, values(values)
|
, values(values)
|
||||||
|
, equalsSignLocation(equalsSignLocation)
|
||||||
{
|
{
|
||||||
if (bool(equalsSignLocation))
|
|
||||||
{
|
|
||||||
hasEqualsSign = true;
|
|
||||||
this->equalsSignLocation = *equalsSignLocation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstStatLocal::visit(AstVisitor* visitor)
|
void AstStatLocal::visit(AstVisitor* visitor)
|
||||||
|
@ -750,9 +738,8 @@ void AstStatError::visit(AstVisitor* visitor)
|
||||||
AstTypeReference::AstTypeReference(
|
AstTypeReference::AstTypeReference(
|
||||||
const Location& location, std::optional<AstName> prefix, AstName name, bool hasParameterList, const AstArray<AstTypeOrPack>& parameters)
|
const Location& location, std::optional<AstName> prefix, AstName name, bool hasParameterList, const AstArray<AstTypeOrPack>& parameters)
|
||||||
: AstType(ClassIndex(), location)
|
: AstType(ClassIndex(), location)
|
||||||
, hasPrefix(bool(prefix))
|
|
||||||
, hasParameterList(hasParameterList)
|
, hasParameterList(hasParameterList)
|
||||||
, prefix(prefix ? *prefix : AstName())
|
, prefix(prefix)
|
||||||
, name(name)
|
, name(name)
|
||||||
, parameters(parameters)
|
, parameters(parameters)
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,11 +101,6 @@ Lexeme::Lexeme(const Location& location, Type type, const char* name)
|
||||||
LUAU_ASSERT(type == Name || (type >= Reserved_BEGIN && type < Lexeme::Reserved_END));
|
LUAU_ASSERT(type == Name || (type >= Reserved_BEGIN && type < Lexeme::Reserved_END));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isComment(const Lexeme& lexeme)
|
|
||||||
{
|
|
||||||
return lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* kReserved[] = {"and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or",
|
static const char* kReserved[] = {"and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or",
|
||||||
"repeat", "return", "then", "true", "until", "while"};
|
"repeat", "return", "then", "true", "until", "while"};
|
||||||
|
|
||||||
|
@ -282,11 +277,6 @@ AstName AstNameTable::get(const char* name) const
|
||||||
return getWithType(name, strlen(name)).first;
|
return getWithType(name, strlen(name)).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isSpace(char ch)
|
|
||||||
{
|
|
||||||
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '\v' || ch == '\f';
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isAlpha(char ch)
|
inline bool isAlpha(char ch)
|
||||||
{
|
{
|
||||||
// use or trick to convert to lower case and unsigned comparison to do range check
|
// use or trick to convert to lower case and unsigned comparison to do range check
|
||||||
|
@ -372,7 +362,7 @@ const Lexeme& Lexer::next(bool skipComments)
|
||||||
prevLocation = lexeme.location;
|
prevLocation = lexeme.location;
|
||||||
|
|
||||||
lexeme = readNext();
|
lexeme = readNext();
|
||||||
} while (skipComments && isComment(lexeme));
|
} while (skipComments && (lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment));
|
||||||
|
|
||||||
return lexeme;
|
return lexeme;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,15 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseSingletonTypes, false)
|
LUAU_FASTFLAGVARIABLE(LuauParseSingletonTypes, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseTypeAliasDefaults, false)
|
LUAU_FASTFLAGVARIABLE(LuauParseTypeAliasDefaults, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseRecoverTypePackEllipsis, false)
|
LUAU_FASTFLAGVARIABLE(LuauParseRecoverTypePackEllipsis, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStartingBrokenComment, false)
|
LUAU_FASTFLAGVARIABLE(LuauParseAllHotComments, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTableFieldFunctionDebugname, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
inline bool isSpace(char ch)
|
|
||||||
{
|
|
||||||
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '\v' || ch == '\f';
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isComment(const Lexeme& lexeme)
|
static bool isComment(const Lexeme& lexeme)
|
||||||
{
|
{
|
||||||
|
LUAU_ASSERT(!FFlag::LuauParseAllHotComments);
|
||||||
return lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment;
|
return lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,31 +148,37 @@ ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& n
|
||||||
{
|
{
|
||||||
LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser");
|
LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser");
|
||||||
|
|
||||||
Parser p(buffer, bufferSize, names, allocator);
|
Parser p(buffer, bufferSize, names, allocator, FFlag::LuauParseAllHotComments ? options : ParseOptions());
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::vector<std::string> hotcomments;
|
if (FFlag::LuauParseAllHotComments)
|
||||||
|
|
||||||
while (isComment(p.lexer.current()) || p.lexer.current().type == Lexeme::BrokenComment)
|
|
||||||
{
|
{
|
||||||
const char* text = p.lexer.current().data;
|
AstStatBlock* root = p.parseChunk();
|
||||||
unsigned int length = p.lexer.current().length;
|
|
||||||
|
|
||||||
if (length && text[0] == '!')
|
return ParseResult{root, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations)};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<HotComment> hotcomments;
|
||||||
|
|
||||||
|
while (isComment(p.lexer.current()) || p.lexer.current().type == Lexeme::BrokenComment)
|
||||||
{
|
{
|
||||||
unsigned int end = length;
|
const char* text = p.lexer.current().data;
|
||||||
while (end > 0 && isSpace(text[end - 1]))
|
unsigned int length = p.lexer.current().length;
|
||||||
--end;
|
|
||||||
|
|
||||||
hotcomments.push_back(std::string(text + 1, text + end));
|
if (length && text[0] == '!')
|
||||||
}
|
{
|
||||||
|
unsigned int end = length;
|
||||||
|
while (end > 0 && isSpace(text[end - 1]))
|
||||||
|
--end;
|
||||||
|
|
||||||
const Lexeme::Type type = p.lexer.current().type;
|
hotcomments.push_back({true, p.lexer.current().location, std::string(text + 1, text + end)});
|
||||||
const Location loc = p.lexer.current().location;
|
}
|
||||||
|
|
||||||
|
const Lexeme::Type type = p.lexer.current().type;
|
||||||
|
const Location loc = p.lexer.current().location;
|
||||||
|
|
||||||
if (FFlag::LuauStartingBrokenComment)
|
|
||||||
{
|
|
||||||
if (options.captureComments)
|
if (options.captureComments)
|
||||||
p.commentLocations.push_back(Comment{type, loc});
|
p.commentLocations.push_back(Comment{type, loc});
|
||||||
|
|
||||||
|
@ -184,22 +187,15 @@ ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& n
|
||||||
|
|
||||||
p.lexer.next();
|
p.lexer.next();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
p.lexer.next();
|
|
||||||
|
|
||||||
if (options.captureComments)
|
p.lexer.setSkipComments(true);
|
||||||
p.commentLocations.push_back(Comment{type, loc});
|
|
||||||
}
|
p.options = options;
|
||||||
|
|
||||||
|
AstStatBlock* root = p.parseChunk();
|
||||||
|
|
||||||
|
return ParseResult{root, hotcomments, p.parseErrors, std::move(p.commentLocations)};
|
||||||
}
|
}
|
||||||
|
|
||||||
p.lexer.setSkipComments(true);
|
|
||||||
|
|
||||||
p.options = options;
|
|
||||||
|
|
||||||
AstStatBlock* root = p.parseChunk();
|
|
||||||
|
|
||||||
return ParseResult{root, hotcomments, p.parseErrors, std::move(p.commentLocations)};
|
|
||||||
}
|
}
|
||||||
catch (ParseError& err)
|
catch (ParseError& err)
|
||||||
{
|
{
|
||||||
|
@ -210,8 +206,9 @@ ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator)
|
Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, const ParseOptions& options)
|
||||||
: lexer(buffer, bufferSize, names)
|
: options(options)
|
||||||
|
, lexer(buffer, bufferSize, names)
|
||||||
, allocator(allocator)
|
, allocator(allocator)
|
||||||
, recursionCounter(0)
|
, recursionCounter(0)
|
||||||
, endMismatchSuspect(Location(), Lexeme::Eof)
|
, endMismatchSuspect(Location(), Lexeme::Eof)
|
||||||
|
@ -224,14 +221,20 @@ Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Alloc
|
||||||
|
|
||||||
nameSelf = names.addStatic("self");
|
nameSelf = names.addStatic("self");
|
||||||
nameNumber = names.addStatic("number");
|
nameNumber = names.addStatic("number");
|
||||||
nameError = names.addStatic(errorName);
|
nameError = names.addStatic(kParseNameError);
|
||||||
nameNil = names.getOrAdd("nil"); // nil is a reserved keyword
|
nameNil = names.getOrAdd("nil"); // nil is a reserved keyword
|
||||||
|
|
||||||
matchRecoveryStopOnToken.assign(Lexeme::Type::Reserved_END, 0);
|
matchRecoveryStopOnToken.assign(Lexeme::Type::Reserved_END, 0);
|
||||||
matchRecoveryStopOnToken[Lexeme::Type::Eof] = 1;
|
matchRecoveryStopOnToken[Lexeme::Type::Eof] = 1;
|
||||||
|
|
||||||
|
if (FFlag::LuauParseAllHotComments)
|
||||||
|
lexer.setSkipComments(true);
|
||||||
|
|
||||||
// read first lexeme
|
// read first lexeme
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
|
// all hot comments parsed after the first non-comment lexeme are special in that they don't affect type checking / linting mode
|
||||||
|
hotcommentHeader = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parser::blockFollow(const Lexeme& l)
|
bool Parser::blockFollow(const Lexeme& l)
|
||||||
|
@ -396,7 +399,9 @@ AstStat* Parser::parseIf()
|
||||||
AstExpr* cond = parseExpr();
|
AstExpr* cond = parseExpr();
|
||||||
|
|
||||||
Lexeme matchThen = lexer.current();
|
Lexeme matchThen = lexer.current();
|
||||||
bool hasThen = expectAndConsume(Lexeme::ReservedThen, "if statement");
|
std::optional<Location> thenLocation;
|
||||||
|
if (expectAndConsume(Lexeme::ReservedThen, "if statement"))
|
||||||
|
thenLocation = matchThen.location;
|
||||||
|
|
||||||
AstStatBlock* thenbody = parseBlock();
|
AstStatBlock* thenbody = parseBlock();
|
||||||
|
|
||||||
|
@ -434,7 +439,7 @@ AstStat* Parser::parseIf()
|
||||||
hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchThenElse);
|
hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchThenElse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return allocator.alloc<AstStatIf>(Location(start, end), cond, thenbody, elsebody, hasThen, matchThen.location, elseLocation, hasEnd);
|
return allocator.alloc<AstStatIf>(Location(start, end), cond, thenbody, elsebody, thenLocation, elseLocation, hasEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// while exp do block end
|
// while exp do block end
|
||||||
|
@ -769,7 +774,7 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
||||||
{
|
{
|
||||||
// note: `type` token is already parsed for us, so we just need to parse the rest
|
// note: `type` token is already parsed for us, so we just need to parse the rest
|
||||||
|
|
||||||
auto name = parseNameOpt("type name");
|
std::optional<Name> name = parseNameOpt("type name");
|
||||||
|
|
||||||
// Use error name if the name is missing
|
// Use error name if the name is missing
|
||||||
if (!name)
|
if (!name)
|
||||||
|
@ -925,7 +930,7 @@ AstStat* Parser::parseDeclaration(const Location& start)
|
||||||
|
|
||||||
return allocator.alloc<AstStatDeclareClass>(Location(classStart, classEnd), className.name, superName, copy(props));
|
return allocator.alloc<AstStatDeclareClass>(Location(classStart, classEnd), className.name, superName, copy(props));
|
||||||
}
|
}
|
||||||
else if (auto globalName = parseNameOpt("global variable name"))
|
else if (std::optional<Name> globalName = parseNameOpt("global variable name"))
|
||||||
{
|
{
|
||||||
expectAndConsume(':', "global variable declaration");
|
expectAndConsume(':', "global variable declaration");
|
||||||
|
|
||||||
|
@ -1066,7 +1071,7 @@ void Parser::parseExprList(TempVector<AstExpr*>& result)
|
||||||
|
|
||||||
Parser::Binding Parser::parseBinding()
|
Parser::Binding Parser::parseBinding()
|
||||||
{
|
{
|
||||||
auto name = parseNameOpt("variable name");
|
std::optional<Name> name = parseNameOpt("variable name");
|
||||||
|
|
||||||
// Use placeholder if the name is missing
|
// Use placeholder if the name is missing
|
||||||
if (!name)
|
if (!name)
|
||||||
|
@ -1325,7 +1330,7 @@ AstType* Parser::parseTableTypeAnnotation()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto name = parseNameOpt("table field");
|
std::optional<Name> name = parseNameOpt("table field");
|
||||||
|
|
||||||
if (!name)
|
if (!name)
|
||||||
break;
|
break;
|
||||||
|
@ -1422,7 +1427,7 @@ AstType* Parser::parseFunctionTypeAnnotationTail(const Lexeme& begin, AstArray<A
|
||||||
expectAndConsume(Lexeme::SkinnyArrow, "function type");
|
expectAndConsume(Lexeme::SkinnyArrow, "function type");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [endLocation, returnTypeList] = parseReturnTypeAnnotation();
|
auto [endLocation, returnTypeList] = parseReturnTypeAnnotation();
|
||||||
|
|
||||||
AstTypeList paramTypes = AstTypeList{params, varargAnnotation};
|
AstTypeList paramTypes = AstTypeList{params, varargAnnotation};
|
||||||
return allocator.alloc<AstTypeFunction>(Location(begin.location, endLocation), generics, genericPacks, paramTypes, paramNames, returnTypeList);
|
return allocator.alloc<AstTypeFunction>(Location(begin.location, endLocation), generics, genericPacks, paramTypes, paramNames, returnTypeList);
|
||||||
|
@ -1869,7 +1874,7 @@ AstExpr* Parser::parseExpr(unsigned int limit)
|
||||||
// NAME
|
// NAME
|
||||||
AstExpr* Parser::parseNameExpr(const char* context)
|
AstExpr* Parser::parseNameExpr(const char* context)
|
||||||
{
|
{
|
||||||
auto name = parseNameOpt(context);
|
std::optional<Name> name = parseNameOpt(context);
|
||||||
|
|
||||||
if (!name)
|
if (!name)
|
||||||
return allocator.alloc<AstExprError>(lexer.current().location, copy<AstExpr*>({}), unsigned(parseErrors.size() - 1));
|
return allocator.alloc<AstExprError>(lexer.current().location, copy<AstExpr*>({}), unsigned(parseErrors.size() - 1));
|
||||||
|
@ -2233,6 +2238,12 @@ AstExpr* Parser::parseTableConstructor()
|
||||||
AstExpr* key = allocator.alloc<AstExprConstantString>(name.location, nameString);
|
AstExpr* key = allocator.alloc<AstExprConstantString>(name.location, nameString);
|
||||||
AstExpr* value = parseExpr();
|
AstExpr* value = parseExpr();
|
||||||
|
|
||||||
|
if (FFlag::LuauTableFieldFunctionDebugname)
|
||||||
|
{
|
||||||
|
if (AstExprFunction* func = value->as<AstExprFunction>())
|
||||||
|
func->debugname = name.name;
|
||||||
|
}
|
||||||
|
|
||||||
items.push_back({AstExprTable::Item::Record, key, value});
|
items.push_back({AstExprTable::Item::Record, key, value});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2313,7 +2324,7 @@ std::optional<Parser::Name> Parser::parseNameOpt(const char* context)
|
||||||
|
|
||||||
Parser::Name Parser::parseName(const char* context)
|
Parser::Name Parser::parseName(const char* context)
|
||||||
{
|
{
|
||||||
if (auto name = parseNameOpt(context))
|
if (std::optional<Name> name = parseNameOpt(context))
|
||||||
return *name;
|
return *name;
|
||||||
|
|
||||||
Location location = lexer.current().location;
|
Location location = lexer.current().location;
|
||||||
|
@ -2324,7 +2335,7 @@ Parser::Name Parser::parseName(const char* context)
|
||||||
|
|
||||||
Parser::Name Parser::parseIndexName(const char* context, const Position& previous)
|
Parser::Name Parser::parseIndexName(const char* context, const Position& previous)
|
||||||
{
|
{
|
||||||
if (auto name = parseNameOpt(context))
|
if (std::optional<Name> name = parseNameOpt(context))
|
||||||
return *name;
|
return *name;
|
||||||
|
|
||||||
// If we have a reserved keyword next at the same line, assume it's an incomplete name
|
// If we have a reserved keyword next at the same line, assume it's an incomplete name
|
||||||
|
@ -2379,7 +2390,7 @@ std::pair<AstArray<AstGenericType>, AstArray<AstGenericTypePack>> Parser::parseG
|
||||||
|
|
||||||
if (shouldParseTypePackAnnotation(lexer))
|
if (shouldParseTypePackAnnotation(lexer))
|
||||||
{
|
{
|
||||||
auto typePack = parseTypePackAnnotation();
|
AstTypePack* typePack = parseTypePackAnnotation();
|
||||||
|
|
||||||
namePacks.push_back({name, nameLocation, typePack});
|
namePacks.push_back({name, nameLocation, typePack});
|
||||||
}
|
}
|
||||||
|
@ -2451,7 +2462,7 @@ AstArray<AstTypeOrPack> Parser::parseTypeParams()
|
||||||
{
|
{
|
||||||
if (shouldParseTypePackAnnotation(lexer))
|
if (shouldParseTypePackAnnotation(lexer))
|
||||||
{
|
{
|
||||||
auto typePack = parseTypePackAnnotation();
|
AstTypePack* typePack = parseTypePackAnnotation();
|
||||||
|
|
||||||
parameters.push_back({{}, typePack});
|
parameters.push_back({{}, typePack});
|
||||||
}
|
}
|
||||||
|
@ -2821,25 +2832,57 @@ AstTypeError* Parser::reportTypeAnnotationError(const Location& location, const
|
||||||
return allocator.alloc<AstTypeError>(location, types, isMissing, unsigned(parseErrors.size() - 1));
|
return allocator.alloc<AstTypeError>(location, types, isMissing, unsigned(parseErrors.size() - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
const Lexeme& Parser::nextLexeme()
|
void Parser::nextLexeme()
|
||||||
{
|
{
|
||||||
if (options.captureComments)
|
if (options.captureComments)
|
||||||
{
|
{
|
||||||
while (true)
|
if (FFlag::LuauParseAllHotComments)
|
||||||
{
|
{
|
||||||
const Lexeme& lexeme = lexer.next(/*skipComments*/ false);
|
Lexeme::Type type = lexer.next(/* skipComments= */ false).type;
|
||||||
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
|
||||||
// The parser will turn this into a proper syntax error.
|
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
|
||||||
if (lexeme.type == Lexeme::BrokenComment)
|
{
|
||||||
|
const Lexeme& lexeme = lexer.current();
|
||||||
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
||||||
if (isComment(lexeme))
|
|
||||||
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
||||||
else
|
// The parser will turn this into a proper syntax error.
|
||||||
return lexeme;
|
if (lexeme.type == Lexeme::BrokenComment)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting
|
||||||
|
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
|
||||||
|
{
|
||||||
|
const char* text = lexeme.data;
|
||||||
|
|
||||||
|
unsigned int end = lexeme.length;
|
||||||
|
while (end > 0 && isSpace(text[end - 1]))
|
||||||
|
--end;
|
||||||
|
|
||||||
|
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
|
||||||
|
}
|
||||||
|
|
||||||
|
type = lexer.next(/* skipComments= */ false).type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const Lexeme& lexeme = lexer.next(/*skipComments*/ false);
|
||||||
|
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
||||||
|
// The parser will turn this into a proper syntax error.
|
||||||
|
if (lexeme.type == Lexeme::BrokenComment)
|
||||||
|
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
||||||
|
if (isComment(lexeme))
|
||||||
|
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return lexer.next();
|
lexer.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -9,6 +9,12 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,13 @@
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#endif
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#include <Windows.h>
|
||||||
#else
|
#else
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
171
CLI/Repl.cpp
171
CLI/Repl.cpp
|
@ -37,6 +37,8 @@ enum class CompileFormat
|
||||||
Binary
|
Binary
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr int MaxTraversalLimit = 50;
|
||||||
|
|
||||||
struct GlobalOptions
|
struct GlobalOptions
|
||||||
{
|
{
|
||||||
int optimizationLevel = 1;
|
int optimizationLevel = 1;
|
||||||
|
@ -243,10 +245,115 @@ std::string runCode(lua_State* L, const std::string& source)
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replaces the top of the lua stack with the metatable __index for the value
|
||||||
|
// if it exists. Returns true iff __index exists.
|
||||||
|
static bool tryReplaceTopWithIndex(lua_State* L)
|
||||||
|
{
|
||||||
|
if (luaL_getmetafield(L, -1, "__index"))
|
||||||
|
{
|
||||||
|
// Remove the table leaving __index on the top of stack
|
||||||
|
lua_remove(L, -2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This function is similar to lua_gettable, but it avoids calling any
|
||||||
|
// lua callback functions (e.g. __index) which might modify the Lua VM state.
|
||||||
|
static void safeGetTable(lua_State* L, int tableIndex)
|
||||||
|
{
|
||||||
|
lua_pushvalue(L, tableIndex); // Duplicate the table
|
||||||
|
|
||||||
|
// The loop invariant is that the table to search is at -1
|
||||||
|
// and the key is at -2.
|
||||||
|
for (int loopCount = 0;; loopCount++)
|
||||||
|
{
|
||||||
|
lua_pushvalue(L, -2); // Duplicate the key
|
||||||
|
lua_rawget(L, -2); // Try to find the key
|
||||||
|
if (!lua_isnil(L, -1) || loopCount >= MaxTraversalLimit)
|
||||||
|
{
|
||||||
|
// Either the key has been found, and/or we have reached the max traversal limit
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lua_pop(L, 1); // Pop the nil result
|
||||||
|
if (!luaL_getmetafield(L, -1, "__index"))
|
||||||
|
{
|
||||||
|
lua_pushnil(L);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (lua_istable(L, -1))
|
||||||
|
{
|
||||||
|
// Replace the current table being searched with __index table
|
||||||
|
lua_replace(L, -2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lua_pop(L, 1); // Pop the value
|
||||||
|
lua_pushnil(L);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_remove(L, -2); // Remove the table
|
||||||
|
lua_remove(L, -2); // Remove the original key
|
||||||
|
}
|
||||||
|
|
||||||
|
// completePartialMatches finds keys that match the specified 'prefix'
|
||||||
|
// Note: the table/object to be searched must be on the top of the Lua stack
|
||||||
|
static void completePartialMatches(lua_State* L, bool completeOnlyFunctions, const std::string& editBuffer, std::string_view prefix,
|
||||||
|
const AddCompletionCallback& addCompletionCallback)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MaxTraversalLimit && lua_istable(L, -1); i++)
|
||||||
|
{
|
||||||
|
// table, key
|
||||||
|
lua_pushnil(L);
|
||||||
|
|
||||||
|
// Loop over all the keys in the current table
|
||||||
|
while (lua_next(L, -2) != 0)
|
||||||
|
{
|
||||||
|
if (lua_type(L, -2) == LUA_TSTRING)
|
||||||
|
{
|
||||||
|
// table, key, value
|
||||||
|
std::string_view key = lua_tostring(L, -2);
|
||||||
|
int valueType = lua_type(L, -1);
|
||||||
|
|
||||||
|
// If the last separator was a ':' (i.e. a method call) then only functions should be completed.
|
||||||
|
bool requiredValueType = (!completeOnlyFunctions || valueType == LUA_TFUNCTION);
|
||||||
|
|
||||||
|
if (!key.empty() && requiredValueType && Luau::startsWith(key, prefix))
|
||||||
|
{
|
||||||
|
std::string completedComponent(key.substr(prefix.size()));
|
||||||
|
std::string completion(editBuffer + completedComponent);
|
||||||
|
if (valueType == LUA_TFUNCTION)
|
||||||
|
{
|
||||||
|
// Add an opening paren for function calls by default.
|
||||||
|
completion += "(";
|
||||||
|
}
|
||||||
|
addCompletionCallback(completion, std::string(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the current table being searched with an __index table if one exists
|
||||||
|
if (!tryReplaceTopWithIndex(L))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void completeIndexer(lua_State* L, const std::string& editBuffer, const AddCompletionCallback& addCompletionCallback)
|
static void completeIndexer(lua_State* L, const std::string& editBuffer, const AddCompletionCallback& addCompletionCallback)
|
||||||
{
|
{
|
||||||
std::string_view lookup = editBuffer;
|
std::string_view lookup = editBuffer;
|
||||||
char lastSep = 0;
|
bool completeOnlyFunctions = false;
|
||||||
|
|
||||||
|
// Push the global variable table to begin the search
|
||||||
|
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
@ -255,60 +362,26 @@ static void completeIndexer(lua_State* L, const std::string& editBuffer, const A
|
||||||
|
|
||||||
if (sep == std::string_view::npos)
|
if (sep == std::string_view::npos)
|
||||||
{
|
{
|
||||||
// table, key
|
completePartialMatches(L, completeOnlyFunctions, editBuffer, prefix, addCompletionCallback);
|
||||||
lua_pushnil(L);
|
|
||||||
|
|
||||||
while (lua_next(L, -2) != 0)
|
|
||||||
{
|
|
||||||
if (lua_type(L, -2) == LUA_TSTRING)
|
|
||||||
{
|
|
||||||
// table, key, value
|
|
||||||
std::string_view key = lua_tostring(L, -2);
|
|
||||||
int valueType = lua_type(L, -1);
|
|
||||||
|
|
||||||
// If the last separator was a ':' (i.e. a method call) then only functions should be completed.
|
|
||||||
bool requiredValueType = (lastSep != ':' || valueType == LUA_TFUNCTION);
|
|
||||||
|
|
||||||
if (!key.empty() && requiredValueType && Luau::startsWith(key, prefix))
|
|
||||||
{
|
|
||||||
std::string completedComponent(key.substr(prefix.size()));
|
|
||||||
std::string completion(editBuffer + completedComponent);
|
|
||||||
if (valueType == LUA_TFUNCTION)
|
|
||||||
{
|
|
||||||
// Add an opening paren for function calls by default.
|
|
||||||
completion += "(";
|
|
||||||
}
|
|
||||||
addCompletionCallback(completion, std::string(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// find the key in the table
|
// find the key in the table
|
||||||
lua_pushlstring(L, prefix.data(), prefix.size());
|
lua_pushlstring(L, prefix.data(), prefix.size());
|
||||||
lua_rawget(L, -2);
|
safeGetTable(L, -2);
|
||||||
lua_remove(L, -2);
|
lua_remove(L, -2);
|
||||||
|
|
||||||
if (lua_type(L, -1) == LUA_TSTRING)
|
if (lua_istable(L, -1) || tryReplaceTopWithIndex(L))
|
||||||
{
|
{
|
||||||
// Replace the string object with the string class to perform further lookups of string functions
|
completeOnlyFunctions = lookup[sep] == ':';
|
||||||
// Note: We retrieve the string class from _G to prevent issues if the user assigns to `string`.
|
lookup.remove_prefix(sep + 1);
|
||||||
lua_pop(L, 1); // Pop the string instance
|
|
||||||
lua_getglobal(L, "_G");
|
|
||||||
lua_pushlstring(L, "string", 6);
|
|
||||||
lua_rawget(L, -2);
|
|
||||||
lua_remove(L, -2); // Remove the global table
|
|
||||||
LUAU_ASSERT(lua_istable(L, -1));
|
|
||||||
}
|
}
|
||||||
else if (!lua_istable(L, -1))
|
else
|
||||||
|
{
|
||||||
|
// Unable to search for keys, so stop searching
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
lastSep = lookup[sep];
|
|
||||||
lookup.remove_prefix(sep + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,12 +390,6 @@ static void completeIndexer(lua_State* L, const std::string& editBuffer, const A
|
||||||
|
|
||||||
void getCompletions(lua_State* L, const std::string& editBuffer, const AddCompletionCallback& addCompletionCallback)
|
void getCompletions(lua_State* L, const std::string& editBuffer, const AddCompletionCallback& addCompletionCallback)
|
||||||
{
|
{
|
||||||
// look the value up in current global table first
|
|
||||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
|
||||||
completeIndexer(L, editBuffer, addCompletionCallback);
|
|
||||||
|
|
||||||
// and in actual global table after that
|
|
||||||
lua_getglobal(L, "_G");
|
|
||||||
completeIndexer(L, editBuffer, addCompletionCallback);
|
completeIndexer(L, editBuffer, addCompletionCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,9 +432,7 @@ struct LinenoiseScopedHistory
|
||||||
ic_set_history(historyFilepath.c_str(), -1 /* default entries (= 200) */);
|
ic_set_history(historyFilepath.c_str(), -1 /* default entries (= 200) */);
|
||||||
}
|
}
|
||||||
|
|
||||||
~LinenoiseScopedHistory()
|
~LinenoiseScopedHistory() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string historyFilepath;
|
std::string historyFilepath;
|
||||||
};
|
};
|
||||||
|
|
|
@ -104,6 +104,20 @@ if (MSVC AND MSVC_VERSION GREATER_EQUAL 1924)
|
||||||
set_source_files_properties(VM/src/lvmexecute.cpp PROPERTIES COMPILE_FLAGS /d2ssa-pre-)
|
set_source_files_properties(VM/src/lvmexecute.cpp PROPERTIES COMPILE_FLAGS /d2ssa-pre-)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# embed .natvis inside the library debug information
|
||||||
|
if(MSVC)
|
||||||
|
target_link_options(Luau.Ast INTERFACE /NATVIS:${CMAKE_CURRENT_SOURCE_DIR}/tools/natvis/Ast.natvis)
|
||||||
|
target_link_options(Luau.Analysis INTERFACE /NATVIS:${CMAKE_CURRENT_SOURCE_DIR}/tools/natvis/Analysis.natvis)
|
||||||
|
target_link_options(Luau.VM INTERFACE /NATVIS:${CMAKE_CURRENT_SOURCE_DIR}/tools/natvis/VM.natvis)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# make .natvis visible inside the solution
|
||||||
|
if(MSVC_IDE)
|
||||||
|
target_sources(Luau.Ast PRIVATE tools/natvis/Ast.natvis)
|
||||||
|
target_sources(Luau.Analysis PRIVATE tools/natvis/Analysis.natvis)
|
||||||
|
target_sources(Luau.VM PRIVATE tools/natvis/VM.natvis)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(LUAU_BUILD_CLI)
|
if(LUAU_BUILD_CLI)
|
||||||
target_compile_options(Luau.Repl.CLI PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.Repl.CLI PRIVATE ${LUAU_OPTIONS})
|
||||||
target_compile_options(Luau.Analyze.CLI PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.Analyze.CLI PRIVATE ${LUAU_OPTIONS})
|
||||||
|
|
|
@ -8,6 +8,7 @@ target_sources(Luau.Ast PRIVATE
|
||||||
Ast/include/Luau/Location.h
|
Ast/include/Luau/Location.h
|
||||||
Ast/include/Luau/ParseOptions.h
|
Ast/include/Luau/ParseOptions.h
|
||||||
Ast/include/Luau/Parser.h
|
Ast/include/Luau/Parser.h
|
||||||
|
Ast/include/Luau/ParseResult.h
|
||||||
Ast/include/Luau/StringUtils.h
|
Ast/include/Luau/StringUtils.h
|
||||||
Ast/include/Luau/TimeTrace.h
|
Ast/include/Luau/TimeTrace.h
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,9 @@ const char* luau_ident = "$Luau: Copyright (C) 2019-2022 Roblox Corporation $\n"
|
||||||
static Table* getcurrenv(lua_State* L)
|
static Table* getcurrenv(lua_State* L)
|
||||||
{
|
{
|
||||||
if (L->ci == L->base_ci) /* no enclosing function? */
|
if (L->ci == L->base_ci) /* no enclosing function? */
|
||||||
return hvalue(gt(L)); /* use global table as environment */
|
return L->gt; /* use global table as environment */
|
||||||
else
|
else
|
||||||
{
|
return curr_func(L)->env;
|
||||||
Closure* func = curr_func(L);
|
|
||||||
return func->env;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static LUAU_NOINLINE TValue* pseudo2addr(lua_State* L, int idx)
|
static LUAU_NOINLINE TValue* pseudo2addr(lua_State* L, int idx)
|
||||||
|
@ -53,11 +50,14 @@ static LUAU_NOINLINE TValue* pseudo2addr(lua_State* L, int idx)
|
||||||
return registry(L);
|
return registry(L);
|
||||||
case LUA_ENVIRONINDEX:
|
case LUA_ENVIRONINDEX:
|
||||||
{
|
{
|
||||||
sethvalue(L, &L->env, getcurrenv(L));
|
sethvalue(L, &L->global->pseudotemp, getcurrenv(L));
|
||||||
return &L->env;
|
return &L->global->pseudotemp;
|
||||||
}
|
}
|
||||||
case LUA_GLOBALSINDEX:
|
case LUA_GLOBALSINDEX:
|
||||||
return gt(L);
|
{
|
||||||
|
sethvalue(L, &L->global->pseudotemp, L->gt);
|
||||||
|
return &L->global->pseudotemp;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
Closure* func = curr_func(L);
|
Closure* func = curr_func(L);
|
||||||
|
@ -237,6 +237,11 @@ void lua_replace(lua_State* L, int idx)
|
||||||
func->env = hvalue(L->top - 1);
|
func->env = hvalue(L->top - 1);
|
||||||
luaC_barrier(L, func, L->top - 1);
|
luaC_barrier(L, func, L->top - 1);
|
||||||
}
|
}
|
||||||
|
else if (idx == LUA_GLOBALSINDEX)
|
||||||
|
{
|
||||||
|
api_check(L, ttistable(L->top - 1));
|
||||||
|
L->gt = hvalue(L->top - 1);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setobj(L, o, L->top - 1);
|
setobj(L, o, L->top - 1);
|
||||||
|
@ -783,7 +788,7 @@ void lua_getfenv(lua_State* L, int idx)
|
||||||
sethvalue(L, L->top, clvalue(o)->env);
|
sethvalue(L, L->top, clvalue(o)->env);
|
||||||
break;
|
break;
|
||||||
case LUA_TTHREAD:
|
case LUA_TTHREAD:
|
||||||
setobj2s(L, L->top, gt(thvalue(o)));
|
sethvalue(L, L->top, thvalue(o)->gt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
setnilvalue(L->top);
|
setnilvalue(L->top);
|
||||||
|
@ -914,7 +919,7 @@ int lua_setfenv(lua_State* L, int idx)
|
||||||
clvalue(o)->env = hvalue(L->top - 1);
|
clvalue(o)->env = hvalue(L->top - 1);
|
||||||
break;
|
break;
|
||||||
case LUA_TTHREAD:
|
case LUA_TTHREAD:
|
||||||
sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1));
|
thvalue(o)->gt = hvalue(L->top - 1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
res = 0;
|
res = 0;
|
||||||
|
|
|
@ -419,7 +419,7 @@ static void getcoverage(Proto* p, int depth, int* buffer, size_t size, void* con
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* debugname = p->debugname ? getstr(p->debugname) : NULL;
|
const char* debugname = p->debugname ? getstr(p->debugname) : NULL;
|
||||||
int linedefined = luaG_getline(p, 0);
|
int linedefined = getlinedefined(p);
|
||||||
|
|
||||||
callback(context, debugname, linedefined, depth, buffer, size);
|
callback(context, debugname, linedefined, depth, buffer, size);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(LuauReduceStackReallocs)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** {======================================================
|
** {======================================================
|
||||||
** Error-recovery functions
|
** Error-recovery functions
|
||||||
|
@ -164,13 +166,14 @@ static void correctstack(lua_State* L, TValue* oldstack)
|
||||||
void luaD_reallocstack(lua_State* L, int newsize)
|
void luaD_reallocstack(lua_State* L, int newsize)
|
||||||
{
|
{
|
||||||
TValue* oldstack = L->stack;
|
TValue* oldstack = L->stack;
|
||||||
int realsize = newsize + 1 + EXTRA_STACK;
|
int realsize = newsize + (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK);
|
||||||
LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
|
LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK));
|
||||||
luaM_reallocarray(L, L->stack, L->stacksize, realsize, TValue, L->memcat);
|
luaM_reallocarray(L, L->stack, L->stacksize, realsize, TValue, L->memcat);
|
||||||
|
TValue* newstack = L->stack;
|
||||||
for (int i = L->stacksize; i < realsize; i++)
|
for (int i = L->stacksize; i < realsize; i++)
|
||||||
setnilvalue(L->stack + i); /* erase new segment */
|
setnilvalue(newstack + i); /* erase new segment */
|
||||||
L->stacksize = realsize;
|
L->stacksize = realsize;
|
||||||
L->stack_last = L->stack + newsize;
|
L->stack_last = newstack + newsize;
|
||||||
correctstack(L, oldstack);
|
correctstack(L, oldstack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +515,7 @@ static void callerrfunc(lua_State* L, void* ud)
|
||||||
|
|
||||||
static void restore_stack_limit(lua_State* L)
|
static void restore_stack_limit(lua_State* L)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
|
LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK));
|
||||||
if (L->size_ci > LUAI_MAXCALLS)
|
if (L->size_ci > LUAI_MAXCALLS)
|
||||||
{ /* there was an overflow? */
|
{ /* there was an overflow? */
|
||||||
int inuse = cast_int(L->ci - L->base_ci);
|
int inuse = cast_int(L->ci - L->base_ci);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \
|
if ((char*)L->stack_last - (char*)L->top <= (n) * (int)sizeof(TValue)) \
|
||||||
luaD_growstack(L, n); \
|
luaD_growstack(L, n); \
|
||||||
else \
|
else \
|
||||||
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));
|
condhardstacktests(luaD_reallocstack(L, L->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK)));
|
||||||
|
|
||||||
#define incr_top(L) \
|
#define incr_top(L) \
|
||||||
{ \
|
{ \
|
||||||
|
|
|
@ -268,7 +268,7 @@ static void traverseclosure(global_State* g, Closure* cl)
|
||||||
|
|
||||||
static void traversestack(global_State* g, lua_State* l, bool clearstack)
|
static void traversestack(global_State* g, lua_State* l, bool clearstack)
|
||||||
{
|
{
|
||||||
markvalue(g, gt(l));
|
markobject(g, l->gt);
|
||||||
if (l->namecall)
|
if (l->namecall)
|
||||||
stringmark(l->namecall);
|
stringmark(l->namecall);
|
||||||
for (StkId o = l->stack; o < l->top; o++)
|
for (StkId o = l->stack; o < l->top; o++)
|
||||||
|
@ -643,7 +643,7 @@ static void markroot(lua_State* L)
|
||||||
g->weak = NULL;
|
g->weak = NULL;
|
||||||
markobject(g, g->mainthread);
|
markobject(g, g->mainthread);
|
||||||
/* make global table be traversed before main stack */
|
/* make global table be traversed before main stack */
|
||||||
markvalue(g, gt(g->mainthread));
|
markobject(g, g->mainthread->gt);
|
||||||
markvalue(g, registry(L));
|
markvalue(g, registry(L));
|
||||||
markmt(g);
|
markmt(g);
|
||||||
g->gcstate = GCSpropagate;
|
g->gcstate = GCSpropagate;
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
|
|
||||||
#define luaC_checkGC(L) \
|
#define luaC_checkGC(L) \
|
||||||
{ \
|
{ \
|
||||||
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
|
condhardstacktests(luaD_reallocstack(L, L->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK))); \
|
||||||
if (L->global->totalbytes >= L->global->GCthreshold) \
|
if (L->global->totalbytes >= L->global->GCthreshold) \
|
||||||
{ \
|
{ \
|
||||||
condhardmemtests(luaC_validate(L), 1); \
|
condhardmemtests(luaC_validate(L), 1); \
|
||||||
|
|
|
@ -88,7 +88,7 @@ static void validateclosure(global_State* g, Closure* cl)
|
||||||
|
|
||||||
static void validatestack(global_State* g, lua_State* l)
|
static void validatestack(global_State* g, lua_State* l)
|
||||||
{
|
{
|
||||||
validateref(g, obj2gco(l), gt(l));
|
validateobjref(g, obj2gco(l), obj2gco(l->gt));
|
||||||
|
|
||||||
for (CallInfo* ci = l->base_ci; ci <= l->ci; ++ci)
|
for (CallInfo* ci = l->base_ci; ci <= l->ci; ++ci)
|
||||||
{
|
{
|
||||||
|
@ -370,6 +370,7 @@ static void dumpclosure(FILE* f, Closure* cl)
|
||||||
|
|
||||||
fprintf(f, ",\"env\":");
|
fprintf(f, ",\"env\":");
|
||||||
dumpref(f, obj2gco(cl->env));
|
dumpref(f, obj2gco(cl->env));
|
||||||
|
|
||||||
if (cl->isC)
|
if (cl->isC)
|
||||||
{
|
{
|
||||||
if (cl->nupvalues)
|
if (cl->nupvalues)
|
||||||
|
@ -411,11 +412,8 @@ static void dumpthread(FILE* f, lua_State* th)
|
||||||
|
|
||||||
fprintf(f, "{\"type\":\"thread\",\"cat\":%d,\"size\":%d", th->memcat, int(size));
|
fprintf(f, "{\"type\":\"thread\",\"cat\":%d,\"size\":%d", th->memcat, int(size));
|
||||||
|
|
||||||
if (iscollectable(&th->l_gt))
|
fprintf(f, ",\"env\":");
|
||||||
{
|
dumpref(f, obj2gco(th->gt));
|
||||||
fprintf(f, ",\"env\":");
|
|
||||||
dumpref(f, gcvalue(&th->l_gt));
|
|
||||||
}
|
|
||||||
|
|
||||||
Closure* tcl = 0;
|
Closure* tcl = 0;
|
||||||
for (CallInfo* ci = th->base_ci; ci <= th->ci; ++ci)
|
for (CallInfo* ci = th->base_ci; ci <= th->ci; ++ci)
|
||||||
|
|
|
@ -3,6 +3,12 @@
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "ldebug.h"
|
#include "ldebug.h"
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauGcPagedSweep)
|
LUAU_FASTFLAG(LuauGcPagedSweep)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauReduceStackReallocs, false)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Main thread combines a thread state and the global state
|
** Main thread combines a thread state and the global state
|
||||||
|
@ -31,10 +32,11 @@ static void stack_init(lua_State* L1, lua_State* L)
|
||||||
/* initialize stack array */
|
/* initialize stack array */
|
||||||
L1->stack = luaM_newarray(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue, L1->memcat);
|
L1->stack = luaM_newarray(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue, L1->memcat);
|
||||||
L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
|
L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
|
||||||
|
TValue* stack = L1->stack;
|
||||||
for (int i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
for (int i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
||||||
setnilvalue(L1->stack + i); /* erase new stack */
|
setnilvalue(stack + i); /* erase new stack */
|
||||||
L1->top = L1->stack;
|
L1->top = stack;
|
||||||
L1->stack_last = L1->stack + (L1->stacksize - EXTRA_STACK) - 1;
|
L1->stack_last = stack + (L1->stacksize - (FFlag::LuauReduceStackReallocs ? EXTRA_STACK : 1 + EXTRA_STACK));
|
||||||
/* initialize first ci */
|
/* initialize first ci */
|
||||||
L1->ci->func = L1->top;
|
L1->ci->func = L1->top;
|
||||||
setnilvalue(L1->top++); /* `function' entry for this `ci' */
|
setnilvalue(L1->top++); /* `function' entry for this `ci' */
|
||||||
|
@ -55,7 +57,7 @@ static void f_luaopen(lua_State* L, void* ud)
|
||||||
{
|
{
|
||||||
global_State* g = L->global;
|
global_State* g = L->global;
|
||||||
stack_init(L, L); /* init stack */
|
stack_init(L, L); /* init stack */
|
||||||
sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */
|
L->gt = luaH_new(L, 0, 2); /* table of globals */
|
||||||
sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */
|
sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */
|
||||||
luaS_resize(L, LUA_MINSTRTABSIZE); /* initial size of string table */
|
luaS_resize(L, LUA_MINSTRTABSIZE); /* initial size of string table */
|
||||||
luaT_init(L);
|
luaT_init(L);
|
||||||
|
@ -69,6 +71,7 @@ static void preinit_state(lua_State* L, global_State* g)
|
||||||
L->global = g;
|
L->global = g;
|
||||||
L->stack = NULL;
|
L->stack = NULL;
|
||||||
L->stacksize = 0;
|
L->stacksize = 0;
|
||||||
|
L->gt = NULL;
|
||||||
L->openupval = NULL;
|
L->openupval = NULL;
|
||||||
L->size_ci = 0;
|
L->size_ci = 0;
|
||||||
L->nCcalls = L->baseCcalls = 0;
|
L->nCcalls = L->baseCcalls = 0;
|
||||||
|
@ -80,7 +83,6 @@ static void preinit_state(lua_State* L, global_State* g)
|
||||||
L->stackstate = 0;
|
L->stackstate = 0;
|
||||||
L->activememcat = 0;
|
L->activememcat = 0;
|
||||||
L->userdata = NULL;
|
L->userdata = NULL;
|
||||||
setnilvalue(gt(L));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_state(lua_State* L)
|
static void close_state(lua_State* L)
|
||||||
|
@ -116,7 +118,7 @@ lua_State* luaE_newthread(lua_State* L)
|
||||||
preinit_state(L1, L->global);
|
preinit_state(L1, L->global);
|
||||||
L1->activememcat = L->activememcat; // inherit the active memory category
|
L1->activememcat = L->activememcat; // inherit the active memory category
|
||||||
stack_init(L1, L); /* init stack */
|
stack_init(L1, L); /* init stack */
|
||||||
setobj2n(L, gt(L1), gt(L)); /* share table of globals */
|
L1->gt = L->gt; /* share table of globals */
|
||||||
L1->singlestep = L->singlestep;
|
L1->singlestep = L->singlestep;
|
||||||
LUAU_ASSERT(iswhite(obj2gco(L1)));
|
LUAU_ASSERT(iswhite(obj2gco(L1)));
|
||||||
return L1;
|
return L1;
|
||||||
|
@ -144,14 +146,30 @@ void lua_resetthread(lua_State* L)
|
||||||
ci->top = ci->base + LUA_MINSTACK;
|
ci->top = ci->base + LUA_MINSTACK;
|
||||||
setnilvalue(ci->func);
|
setnilvalue(ci->func);
|
||||||
L->ci = ci;
|
L->ci = ci;
|
||||||
luaD_reallocCI(L, BASIC_CI_SIZE);
|
if (FFlag::LuauReduceStackReallocs)
|
||||||
|
{
|
||||||
|
if (L->size_ci != BASIC_CI_SIZE)
|
||||||
|
luaD_reallocCI(L, BASIC_CI_SIZE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaD_reallocCI(L, BASIC_CI_SIZE);
|
||||||
|
}
|
||||||
/* clear thread state */
|
/* clear thread state */
|
||||||
L->status = LUA_OK;
|
L->status = LUA_OK;
|
||||||
L->base = L->ci->base;
|
L->base = L->ci->base;
|
||||||
L->top = L->ci->base;
|
L->top = L->ci->base;
|
||||||
L->nCcalls = L->baseCcalls = 0;
|
L->nCcalls = L->baseCcalls = 0;
|
||||||
/* clear thread stack */
|
/* clear thread stack */
|
||||||
luaD_reallocstack(L, BASIC_STACK_SIZE);
|
if (FFlag::LuauReduceStackReallocs)
|
||||||
|
{
|
||||||
|
if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK)
|
||||||
|
luaD_reallocstack(L, BASIC_STACK_SIZE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaD_reallocstack(L, BASIC_STACK_SIZE);
|
||||||
|
}
|
||||||
for (int i = 0; i < L->stacksize; i++)
|
for (int i = 0; i < L->stacksize; i++)
|
||||||
setnilvalue(L->stack + i);
|
setnilvalue(L->stack + i);
|
||||||
}
|
}
|
||||||
|
@ -193,6 +211,7 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
|
||||||
g->strt.size = 0;
|
g->strt.size = 0;
|
||||||
g->strt.nuse = 0;
|
g->strt.nuse = 0;
|
||||||
g->strt.hash = NULL;
|
g->strt.hash = NULL;
|
||||||
|
setnilvalue(&g->pseudotemp);
|
||||||
setnilvalue(registry(L));
|
setnilvalue(registry(L));
|
||||||
g->gcstate = GCSpause;
|
g->gcstate = GCSpause;
|
||||||
if (!FFlag::LuauGcPagedSweep)
|
if (!FFlag::LuauGcPagedSweep)
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
#include "lobject.h"
|
#include "lobject.h"
|
||||||
#include "ltm.h"
|
#include "ltm.h"
|
||||||
|
|
||||||
/* table of globals */
|
|
||||||
#define gt(L) (&L->l_gt)
|
|
||||||
|
|
||||||
/* registry */
|
/* registry */
|
||||||
#define registry(L) (&L->global->registry)
|
#define registry(L) (&L->global->registry)
|
||||||
|
|
||||||
|
@ -177,6 +174,8 @@ typedef struct global_State
|
||||||
TString* ttname[LUA_T_COUNT]; /* names for basic types */
|
TString* ttname[LUA_T_COUNT]; /* names for basic types */
|
||||||
TString* tmname[TM_N]; /* array with tag-method names */
|
TString* tmname[TM_N]; /* array with tag-method names */
|
||||||
|
|
||||||
|
TValue pseudotemp; /* storage for temporary values used in pseudo2addr */
|
||||||
|
|
||||||
TValue registry; /* registry table, used by lua_ref and LUA_REGISTRYINDEX */
|
TValue registry; /* registry table, used by lua_ref and LUA_REGISTRYINDEX */
|
||||||
int registryfree; /* next free slot in registry */
|
int registryfree; /* next free slot in registry */
|
||||||
|
|
||||||
|
@ -231,8 +230,7 @@ struct lua_State
|
||||||
int cachedslot; /* when table operations or INDEX/NEWINDEX is invoked from Luau, what is the expected slot for lookup? */
|
int cachedslot; /* when table operations or INDEX/NEWINDEX is invoked from Luau, what is the expected slot for lookup? */
|
||||||
|
|
||||||
|
|
||||||
TValue l_gt; /* table of globals */
|
Table* gt; /* table of globals */
|
||||||
TValue env; /* temporary place for environments */
|
|
||||||
UpVal* openupval; /* list of open upvalues in this stack */
|
UpVal* openupval; /* list of open upvalues in this stack */
|
||||||
GCObject* gclist;
|
GCObject* gclist;
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
|
|
||||||
// When calling luau_callTM, we usually push the arguments to the top of the stack.
|
// When calling luau_callTM, we usually push the arguments to the top of the stack.
|
||||||
// This is safe to do for complicated reasons:
|
// This is safe to do for complicated reasons:
|
||||||
// - stack guarantees 1 + EXTRA_STACK room beyond stack_last (see luaD_reallocstack)
|
// - stack guarantees EXTRA_STACK room beyond stack_last (see luaD_reallocstack)
|
||||||
// - stack reallocation copies values past stack_last
|
// - stack reallocation copies values past stack_last
|
||||||
|
|
||||||
// All external function calls that can cause stack realloc or Lua calls have to be wrapped in VM_PROTECT
|
// All external function calls that can cause stack realloc or Lua calls have to be wrapped in VM_PROTECT
|
||||||
|
|
|
@ -116,12 +116,12 @@ static void resolveImportSafe(lua_State* L, Table* env, TValue* k, uint32_t id)
|
||||||
// note: we call getimport with nil propagation which means that accesses to table chains like A.B.C will resolve in nil
|
// note: we call getimport with nil propagation which means that accesses to table chains like A.B.C will resolve in nil
|
||||||
// this is technically not necessary but it reduces the number of exceptions when loading scripts that rely on getfenv/setfenv for global
|
// this is technically not necessary but it reduces the number of exceptions when loading scripts that rely on getfenv/setfenv for global
|
||||||
// injection
|
// injection
|
||||||
luaV_getimport(L, hvalue(gt(L)), self->k, self->id, /* propagatenil= */ true);
|
luaV_getimport(L, L->gt, self->k, self->id, /* propagatenil= */ true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ResolveImport ri = {k, id};
|
ResolveImport ri = {k, id};
|
||||||
if (hvalue(gt(L))->safeenv)
|
if (L->gt->safeenv)
|
||||||
{
|
{
|
||||||
// luaD_pcall will make sure that if any C/Lua calls during import resolution fail, the thread state is restored back
|
// luaD_pcall will make sure that if any C/Lua calls during import resolution fail, the thread state is restored back
|
||||||
int oldTop = lua_gettop(L);
|
int oldTop = lua_gettop(L);
|
||||||
|
@ -171,7 +171,7 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
||||||
L->global->GCthreshold = SIZE_MAX;
|
L->global->GCthreshold = SIZE_MAX;
|
||||||
|
|
||||||
// env is 0 for current environment and a stack index otherwise
|
// env is 0 for current environment and a stack index otherwise
|
||||||
Table* envt = (env == 0) ? hvalue(gt(L)) : hvalue(luaA_toobject(L, env));
|
Table* envt = (env == 0) ? L->gt : hvalue(luaA_toobject(L, env));
|
||||||
|
|
||||||
TString* source = luaS_new(L, chunkname);
|
TString* source = luaS_new(L, chunkname);
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ static void callTMres(lua_State* L, StkId res, const TValue* f, const TValue* p1
|
||||||
{
|
{
|
||||||
ptrdiff_t result = savestack(L, res);
|
ptrdiff_t result = savestack(L, res);
|
||||||
// using stack room beyond top is technically safe here, but for very complicated reasons:
|
// using stack room beyond top is technically safe here, but for very complicated reasons:
|
||||||
// * The stack guarantees 1 + EXTRA_STACK room beyond stack_last (see luaD_reallocstack) will be allocated
|
// * The stack guarantees EXTRA_STACK room beyond stack_last (see luaD_reallocstack) will be allocated
|
||||||
// * we cannot move luaD_checkstack above because the arguments are *sometimes* pointers to the lua
|
// * we cannot move luaD_checkstack above because the arguments are *sometimes* pointers to the lua
|
||||||
// stack and checkstack may invalidate those pointers
|
// stack and checkstack may invalidate those pointers
|
||||||
// * we cannot use savestack/restorestack because the arguments are sometimes on the C++ stack
|
// * we cannot use savestack/restorestack because the arguments are sometimes on the C++ stack
|
||||||
|
@ -76,7 +76,7 @@ static void callTMres(lua_State* L, StkId res, const TValue* f, const TValue* p1
|
||||||
static void callTM(lua_State* L, const TValue* f, const TValue* p1, const TValue* p2, const TValue* p3)
|
static void callTM(lua_State* L, const TValue* f, const TValue* p1, const TValue* p2, const TValue* p3)
|
||||||
{
|
{
|
||||||
// using stack room beyond top is technically safe here, but for very complicated reasons:
|
// using stack room beyond top is technically safe here, but for very complicated reasons:
|
||||||
// * The stack guarantees 1 + EXTRA_STACK room beyond stack_last (see luaD_reallocstack) will be allocated
|
// * The stack guarantees EXTRA_STACK room beyond stack_last (see luaD_reallocstack) will be allocated
|
||||||
// * we cannot move luaD_checkstack above because the arguments are *sometimes* pointers to the lua
|
// * we cannot move luaD_checkstack above because the arguments are *sometimes* pointers to the lua
|
||||||
// stack and checkstack may invalidate those pointers
|
// stack and checkstack may invalidate those pointers
|
||||||
// * we cannot use savestack/restorestack because the arguments are sometimes on the C++ stack
|
// * we cannot use savestack/restorestack because the arguments are sometimes on the C++ stack
|
||||||
|
|
|
@ -32,7 +32,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
|
||||||
Luau::LintOptions lintOptions;
|
Luau::LintOptions lintOptions;
|
||||||
lintOptions.warningMask = ~0ull;
|
lintOptions.warningMask = ~0ull;
|
||||||
|
|
||||||
Luau::lint(parseResult.root, names, typeck.globalScope, nullptr, lintOptions);
|
Luau::lint(parseResult.root, names, typeck.globalScope, nullptr, {}, lintOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -227,7 +227,7 @@ DEFINE_PROTO_FUZZER(const luau::StatBlock& message)
|
||||||
if (kFuzzLinter)
|
if (kFuzzLinter)
|
||||||
{
|
{
|
||||||
Luau::LintOptions lintOptions = {~0u};
|
Luau::LintOptions lintOptions = {~0u};
|
||||||
Luau::lint(parseResult.root, names, sharedEnv.globalScope, module.get(), lintOptions);
|
Luau::lint(parseResult.root, names, sharedEnv.globalScope, module.get(), {}, lintOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Autocomplete.h"
|
#include "Luau/Autocomplete.h"
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
#include "Luau/VisitTypeVar.h"
|
#include "Luau/VisitTypeVar.h"
|
||||||
|
@ -2610,6 +2609,27 @@ a = if temp then even elseif true then temp else e@9
|
||||||
CHECK(ac.entryMap.count("elseif") == 0);
|
CHECK(ac.entryMap.count("elseif") == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_if_else_regression")
|
||||||
|
{
|
||||||
|
ScopedFastFlag FFlagLuauIfElseExprFixCompletionIssue("LuauIfElseExprFixCompletionIssue", true);
|
||||||
|
check(R"(
|
||||||
|
local abcdef = 0;
|
||||||
|
local temp = false
|
||||||
|
local even = true;
|
||||||
|
local a
|
||||||
|
a = if temp then even else@1
|
||||||
|
a = if temp then even else @2
|
||||||
|
a = if temp then even else abc@3
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto ac = autocomplete('1');
|
||||||
|
CHECK(ac.entryMap.count("else") == 0);
|
||||||
|
ac = autocomplete('2');
|
||||||
|
CHECK(ac.entryMap.count("else") == 0);
|
||||||
|
ac = autocomplete('3');
|
||||||
|
CHECK(ac.entryMap.count("abcdef"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ACFixture, "autocomplete_explicit_type_pack")
|
TEST_CASE_FIXTURE(ACFixture, "autocomplete_explicit_type_pack")
|
||||||
{
|
{
|
||||||
check(R"(
|
check(R"(
|
||||||
|
|
|
@ -10,6 +10,11 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace Luau
|
||||||
|
{
|
||||||
|
std::string rep(const std::string& s, size_t n);
|
||||||
|
}
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
static std::string compileFunction(const char* source, uint32_t id)
|
static std::string compileFunction(const char* source, uint32_t id)
|
||||||
|
@ -1960,15 +1965,6 @@ RETURN R8 -1
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string rep(const std::string& s, size_t n)
|
|
||||||
{
|
|
||||||
std::string r;
|
|
||||||
r.reserve(s.length() * n);
|
|
||||||
for (size_t i = 0; i < n; ++i)
|
|
||||||
r += s;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("RecursionParse")
|
TEST_CASE("RecursionParse")
|
||||||
{
|
{
|
||||||
// The test forcibly pushes the stack limit during compilation; in NoOpt, the stack consumption is much larger so we need to reduce the limit to
|
// The test forcibly pushes the stack limit during compilation; in NoOpt, the stack consumption is much larger so we need to reduce the limit to
|
||||||
|
|
|
@ -492,6 +492,8 @@ TEST_CASE("DateTime")
|
||||||
|
|
||||||
TEST_CASE("Debug")
|
TEST_CASE("Debug")
|
||||||
{
|
{
|
||||||
|
ScopedFastFlag luauTableFieldFunctionDebugname{"LuauTableFieldFunctionDebugname", true};
|
||||||
|
|
||||||
runConformance("debug.lua");
|
runConformance("debug.lua");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -890,6 +892,12 @@ TEST_CASE("Coverage")
|
||||||
lua_pushstring(L, function);
|
lua_pushstring(L, function);
|
||||||
lua_setfield(L, -2, "name");
|
lua_setfield(L, -2, "name");
|
||||||
|
|
||||||
|
lua_pushinteger(L, linedefined);
|
||||||
|
lua_setfield(L, -2, "linedefined");
|
||||||
|
|
||||||
|
lua_pushinteger(L, depth);
|
||||||
|
lua_setfield(L, -2, "depth");
|
||||||
|
|
||||||
for (size_t i = 0; i < size; ++i)
|
for (size_t i = 0; i < size; ++i)
|
||||||
if (hits[i] != -1)
|
if (hits[i] != -1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "Fixture.h"
|
#include "Fixture.h"
|
||||||
|
|
||||||
#include "Luau/AstQuery.h"
|
#include "Luau/AstQuery.h"
|
||||||
|
#include "Luau/Parser.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
#include "Luau/TypeAttach.h"
|
#include "Luau/TypeAttach.h"
|
||||||
#include "Luau/Transpiler.h"
|
#include "Luau/Transpiler.h"
|
||||||
|
@ -112,7 +113,7 @@ AstStatBlock* Fixture::parse(const std::string& source, const ParseOptions& pars
|
||||||
sourceModule->name = fromString(mainModuleName);
|
sourceModule->name = fromString(mainModuleName);
|
||||||
sourceModule->root = result.root;
|
sourceModule->root = result.root;
|
||||||
sourceModule->mode = parseMode(result.hotcomments);
|
sourceModule->mode = parseMode(result.hotcomments);
|
||||||
sourceModule->ignoreLints = LintWarning::parseMask(result.hotcomments);
|
sourceModule->hotcomments = std::move(result.hotcomments);
|
||||||
|
|
||||||
if (!result.errors.empty())
|
if (!result.errors.empty())
|
||||||
{
|
{
|
||||||
|
@ -157,6 +158,7 @@ CheckResult Fixture::check(const std::string& source)
|
||||||
LintResult Fixture::lint(const std::string& source, const std::optional<LintOptions>& lintOptions)
|
LintResult Fixture::lint(const std::string& source, const std::optional<LintOptions>& lintOptions)
|
||||||
{
|
{
|
||||||
ParseOptions parseOptions;
|
ParseOptions parseOptions;
|
||||||
|
parseOptions.captureComments = true;
|
||||||
configResolver.defaultConfig.mode = Mode::Nonstrict;
|
configResolver.defaultConfig.mode = Mode::Nonstrict;
|
||||||
parse(source, parseOptions);
|
parse(source, parseOptions);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "Luau/Linter.h"
|
#include "Luau/Linter.h"
|
||||||
#include "Luau/Location.h"
|
#include "Luau/Location.h"
|
||||||
#include "Luau/ModuleResolver.h"
|
#include "Luau/ModuleResolver.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/ToString.h"
|
#include "Luau/ToString.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "Luau/AstQuery.h"
|
#include "Luau/AstQuery.h"
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Frontend.h"
|
#include "Luau/Frontend.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/RequireTracer.h"
|
#include "Luau/RequireTracer.h"
|
||||||
|
|
||||||
#include "Fixture.h"
|
#include "Fixture.h"
|
||||||
|
@ -897,8 +896,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "clearStats")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(FrontendFixture, "typecheck_twice_for_ast_types")
|
TEST_CASE_FIXTURE(FrontendFixture, "typecheck_twice_for_ast_types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sffs("LuauTypeCheckTwice", true);
|
|
||||||
|
|
||||||
fileResolver.source["Module/A"] = R"(
|
fileResolver.source["Module/A"] = R"(
|
||||||
local a = 1
|
local a = 1
|
||||||
)";
|
)";
|
||||||
|
|
|
@ -1395,9 +1395,7 @@ TEST_CASE_FIXTURE(Fixture, "DeprecatedApi")
|
||||||
|
|
||||||
TypeId colorType = typeChecker.globalTypes.addType(TableTypeVar{{}, std::nullopt, typeChecker.globalScope->level, Luau::TableState::Sealed});
|
TypeId colorType = typeChecker.globalTypes.addType(TableTypeVar{{}, std::nullopt, typeChecker.globalScope->level, Luau::TableState::Sealed});
|
||||||
|
|
||||||
getMutable<TableTypeVar>(colorType)->props = {
|
getMutable<TableTypeVar>(colorType)->props = {{"toHSV", {typeChecker.anyType, /* deprecated= */ true, "Color3:ToHSV"}}};
|
||||||
{"toHSV", {typeChecker.anyType, /* deprecated= */ true, "Color3:ToHSV"} }
|
|
||||||
};
|
|
||||||
|
|
||||||
addGlobalBinding(typeChecker, "Color3", Binding{colorType, {}});
|
addGlobalBinding(typeChecker, "Color3", Binding{colorType, {}});
|
||||||
|
|
||||||
|
@ -1554,8 +1552,46 @@ _ = (math.random() < 0.5 and false) or 42 -- currently ignored
|
||||||
)");
|
)");
|
||||||
|
|
||||||
REQUIRE_EQ(result.warnings.size(), 2);
|
REQUIRE_EQ(result.warnings.size(), 2);
|
||||||
CHECK_EQ(result.warnings[0].text, "The and-or expression always evaluates to the second alternative because the first alternative is false; consider using if-then-else expression instead");
|
CHECK_EQ(result.warnings[0].text, "The and-or expression always evaluates to the second alternative because the first alternative is false; "
|
||||||
CHECK_EQ(result.warnings[1].text, "The and-or expression always evaluates to the second alternative because the first alternative is nil; consider using if-then-else expression instead");
|
"consider using if-then-else expression instead");
|
||||||
|
CHECK_EQ(result.warnings[1].text, "The and-or expression always evaluates to the second alternative because the first alternative is nil; "
|
||||||
|
"consider using if-then-else expression instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "WrongComment")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff("LuauParseAllHotComments", true);
|
||||||
|
|
||||||
|
LintResult result = lint(R"(
|
||||||
|
--!strict
|
||||||
|
--!struct
|
||||||
|
--!nolintGlobal
|
||||||
|
--!nolint Global
|
||||||
|
--!nolint KnownGlobal
|
||||||
|
--!nolint UnknownGlobal
|
||||||
|
--! no more lint
|
||||||
|
--!strict here
|
||||||
|
do end
|
||||||
|
--!nolint
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE_EQ(result.warnings.size(), 6);
|
||||||
|
CHECK_EQ(result.warnings[0].text, "Unknown comment directive 'struct'; did you mean 'strict'?");
|
||||||
|
CHECK_EQ(result.warnings[1].text, "Unknown comment directive 'nolintGlobal'");
|
||||||
|
CHECK_EQ(result.warnings[2].text, "nolint directive refers to unknown lint rule 'Global'");
|
||||||
|
CHECK_EQ(result.warnings[3].text, "nolint directive refers to unknown lint rule 'KnownGlobal'; did you mean 'UnknownGlobal'?");
|
||||||
|
CHECK_EQ(result.warnings[4].text, "Comment directive with the type checking mode has extra symbols at the end of the line");
|
||||||
|
CHECK_EQ(result.warnings[5].text, "Comment directive is ignored because it is placed after the first non-comment token");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "WrongCommentMuteSelf")
|
||||||
|
{
|
||||||
|
LintResult result = lint(R"(
|
||||||
|
--!nolint
|
||||||
|
--!struct
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE_EQ(result.warnings.size(), 0); // --!nolint disables WrongComment lint :)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
#include "Luau/Parser.h"
|
||||||
#include "Luau/TypeInfer.h"
|
|
||||||
|
|
||||||
#include "Fixture.h"
|
#include "Fixture.h"
|
||||||
#include "ScopedFlags.h"
|
#include "ScopedFlags.h"
|
||||||
|
@ -300,8 +299,9 @@ TEST_CASE_FIXTURE(Fixture, "functions_can_have_return_annotations")
|
||||||
AstStatFunction* statFunction = block->body.data[0]->as<AstStatFunction>();
|
AstStatFunction* statFunction = block->body.data[0]->as<AstStatFunction>();
|
||||||
REQUIRE(statFunction != nullptr);
|
REQUIRE(statFunction != nullptr);
|
||||||
|
|
||||||
CHECK_EQ(statFunction->func->returnAnnotation.types.size, 1);
|
REQUIRE(statFunction->func->returnAnnotation.has_value());
|
||||||
CHECK(statFunction->func->returnAnnotation.tailType == nullptr);
|
CHECK_EQ(statFunction->func->returnAnnotation->types.size, 1);
|
||||||
|
CHECK(statFunction->func->returnAnnotation->tailType == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "functions_can_have_a_function_type_annotation")
|
TEST_CASE_FIXTURE(Fixture, "functions_can_have_a_function_type_annotation")
|
||||||
|
@ -316,9 +316,9 @@ TEST_CASE_FIXTURE(Fixture, "functions_can_have_a_function_type_annotation")
|
||||||
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
||||||
REQUIRE(statFunc != nullptr);
|
REQUIRE(statFunc != nullptr);
|
||||||
|
|
||||||
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation.types;
|
REQUIRE(statFunc->func->returnAnnotation.has_value());
|
||||||
REQUIRE(statFunc->func->hasReturnAnnotation);
|
CHECK(statFunc->func->returnAnnotation->tailType == nullptr);
|
||||||
CHECK(statFunc->func->returnAnnotation.tailType == nullptr);
|
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation->types;
|
||||||
REQUIRE(retTypes.size == 1);
|
REQUIRE(retTypes.size == 1);
|
||||||
|
|
||||||
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
||||||
|
@ -337,9 +337,9 @@ TEST_CASE_FIXTURE(Fixture, "function_return_type_should_disambiguate_from_functi
|
||||||
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
||||||
REQUIRE(statFunc != nullptr);
|
REQUIRE(statFunc != nullptr);
|
||||||
|
|
||||||
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation.types;
|
REQUIRE(statFunc->func->returnAnnotation.has_value());
|
||||||
REQUIRE(statFunc->func->hasReturnAnnotation);
|
CHECK(statFunc->func->returnAnnotation->tailType == nullptr);
|
||||||
CHECK(statFunc->func->returnAnnotation.tailType == nullptr);
|
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation->types;
|
||||||
REQUIRE(retTypes.size == 2);
|
REQUIRE(retTypes.size == 2);
|
||||||
|
|
||||||
AstTypeReference* ty0 = retTypes.data[0]->as<AstTypeReference>();
|
AstTypeReference* ty0 = retTypes.data[0]->as<AstTypeReference>();
|
||||||
|
@ -363,9 +363,9 @@ TEST_CASE_FIXTURE(Fixture, "function_return_type_should_parse_as_function_type_a
|
||||||
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
|
||||||
REQUIRE(statFunc != nullptr);
|
REQUIRE(statFunc != nullptr);
|
||||||
|
|
||||||
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation.types;
|
REQUIRE(statFunc->func->returnAnnotation.has_value());
|
||||||
REQUIRE(statFunc->func->hasReturnAnnotation);
|
CHECK(statFunc->func->returnAnnotation->tailType == nullptr);
|
||||||
CHECK(statFunc->func->returnAnnotation.tailType == nullptr);
|
AstArray<AstType*>& retTypes = statFunc->func->returnAnnotation->types;
|
||||||
REQUIRE(retTypes.size == 1);
|
REQUIRE(retTypes.size == 1);
|
||||||
|
|
||||||
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
|
||||||
|
@ -707,12 +707,25 @@ TEST_CASE_FIXTURE(Fixture, "mode_is_unset_if_no_hot_comment")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "sense_hot_comment_on_first_line")
|
TEST_CASE_FIXTURE(Fixture, "sense_hot_comment_on_first_line")
|
||||||
{
|
{
|
||||||
ParseResult result = parseEx(" --!strict ");
|
ParseOptions options;
|
||||||
|
options.captureComments = true;
|
||||||
|
|
||||||
|
ParseResult result = parseEx(" --!strict ", options);
|
||||||
std::optional<Mode> mode = parseMode(result.hotcomments);
|
std::optional<Mode> mode = parseMode(result.hotcomments);
|
||||||
REQUIRE(bool(mode));
|
REQUIRE(bool(mode));
|
||||||
CHECK_EQ(int(*mode), int(Mode::Strict));
|
CHECK_EQ(int(*mode), int(Mode::Strict));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "non_header_hot_comments")
|
||||||
|
{
|
||||||
|
ParseOptions options;
|
||||||
|
options.captureComments = true;
|
||||||
|
|
||||||
|
ParseResult result = parseEx("do end --!strict", options);
|
||||||
|
std::optional<Mode> mode = parseMode(result.hotcomments);
|
||||||
|
REQUIRE(!mode);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "stop_if_line_ends_with_hyphen")
|
TEST_CASE_FIXTURE(Fixture, "stop_if_line_ends_with_hyphen")
|
||||||
{
|
{
|
||||||
CHECK_THROWS_AS(parse(" -"), std::exception);
|
CHECK_THROWS_AS(parse(" -"), std::exception);
|
||||||
|
@ -720,7 +733,10 @@ TEST_CASE_FIXTURE(Fixture, "stop_if_line_ends_with_hyphen")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "nonstrict_mode")
|
TEST_CASE_FIXTURE(Fixture, "nonstrict_mode")
|
||||||
{
|
{
|
||||||
ParseResult result = parseEx("--!nonstrict");
|
ParseOptions options;
|
||||||
|
options.captureComments = true;
|
||||||
|
|
||||||
|
ParseResult result = parseEx("--!nonstrict", options);
|
||||||
CHECK(result.errors.empty());
|
CHECK(result.errors.empty());
|
||||||
std::optional<Mode> mode = parseMode(result.hotcomments);
|
std::optional<Mode> mode = parseMode(result.hotcomments);
|
||||||
REQUIRE(bool(mode));
|
REQUIRE(bool(mode));
|
||||||
|
@ -729,7 +745,10 @@ TEST_CASE_FIXTURE(Fixture, "nonstrict_mode")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "nocheck_mode")
|
TEST_CASE_FIXTURE(Fixture, "nocheck_mode")
|
||||||
{
|
{
|
||||||
ParseResult result = parseEx("--!nocheck");
|
ParseOptions options;
|
||||||
|
options.captureComments = true;
|
||||||
|
|
||||||
|
ParseResult result = parseEx("--!nocheck", options);
|
||||||
CHECK(result.errors.empty());
|
CHECK(result.errors.empty());
|
||||||
std::optional<Mode> mode = parseMode(result.hotcomments);
|
std::optional<Mode> mode = parseMode(result.hotcomments);
|
||||||
REQUIRE(bool(mode));
|
REQUIRE(bool(mode));
|
||||||
|
@ -1498,8 +1517,6 @@ return
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "parse_error_broken_comment")
|
TEST_CASE_FIXTURE(Fixture, "parse_error_broken_comment")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauStartingBrokenComment{"LuauStartingBrokenComment", true};
|
|
||||||
|
|
||||||
const char* expected = "Expected identifier when parsing expression, got unfinished comment";
|
const char* expected = "Expected identifier when parsing expression, got unfinished comment";
|
||||||
|
|
||||||
matchParseError("--[[unfinished work", expected);
|
matchParseError("--[[unfinished work", expected);
|
||||||
|
|
|
@ -73,7 +73,7 @@ public:
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<lua_State, void (*)(lua_State*)> luaState;
|
std::unique_ptr<lua_State, void (*)(lua_State*)> luaState;
|
||||||
|
|
||||||
// This is a simplicitic and incomplete pretty printer.
|
// This is a simplistic and incomplete pretty printer.
|
||||||
// It is included here to test that the pretty printer hook is being called.
|
// It is included here to test that the pretty printer hook is being called.
|
||||||
// More elaborate tests to ensure correct output can be added if we introduce
|
// More elaborate tests to ensure correct output can be added if we introduce
|
||||||
// a more feature rich pretty printer.
|
// a more feature rich pretty printer.
|
||||||
|
@ -158,12 +158,25 @@ TEST_CASE_FIXTURE(ReplFixture, "CompleteGlobalVariables")
|
||||||
myvariable1 = 5
|
myvariable1 = 5
|
||||||
myvariable2 = 5
|
myvariable2 = 5
|
||||||
)");
|
)");
|
||||||
CompletionSet completions = getCompletionSet("myvar");
|
{
|
||||||
|
// Try to complete globals that are added by the user's script
|
||||||
|
CompletionSet completions = getCompletionSet("myvar");
|
||||||
|
|
||||||
std::string prefix = "";
|
std::string prefix = "";
|
||||||
CHECK(completions.size() == 2);
|
CHECK(completions.size() == 2);
|
||||||
CHECK(checkCompletion(completions, prefix, "myvariable1"));
|
CHECK(checkCompletion(completions, prefix, "myvariable1"));
|
||||||
CHECK(checkCompletion(completions, prefix, "myvariable2"));
|
CHECK(checkCompletion(completions, prefix, "myvariable2"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Try completing some builtin functions
|
||||||
|
CompletionSet completions = getCompletionSet("math.m");
|
||||||
|
|
||||||
|
std::string prefix = "math.";
|
||||||
|
CHECK(completions.size() == 3);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "max("));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "min("));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "modf("));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(ReplFixture, "CompleteTableKeys")
|
TEST_CASE_FIXTURE(ReplFixture, "CompleteTableKeys")
|
||||||
|
@ -206,4 +219,188 @@ TEST_CASE_FIXTURE(ReplFixture, "StringMethods")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplFixture, "TableWithMetatableIndexTable")
|
||||||
|
{
|
||||||
|
runCode(L, R"(
|
||||||
|
-- Create 't' which is a table with a metatable with an __index table
|
||||||
|
mt = {}
|
||||||
|
mt.__index = mt
|
||||||
|
|
||||||
|
t = {}
|
||||||
|
setmetatable(t, mt)
|
||||||
|
|
||||||
|
mt.mtkey1 = {x="x value", y="y value", 1, 2}
|
||||||
|
mt.mtkey2 = 2
|
||||||
|
|
||||||
|
t.tkey1 = {data1 = 2, data2 = "str", 3, 4}
|
||||||
|
t.tkey2 = 4
|
||||||
|
)");
|
||||||
|
{
|
||||||
|
CompletionSet completions = getCompletionSet("t.t");
|
||||||
|
|
||||||
|
std::string prefix = "t.";
|
||||||
|
CHECK(completions.size() == 2);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "tkey1"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "tkey2"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CompletionSet completions = getCompletionSet("t.tkey1.data2:re");
|
||||||
|
|
||||||
|
std::string prefix = "t.tkey1.data2:";
|
||||||
|
CHECK(completions.size() == 2);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "rep("));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "reverse("));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CompletionSet completions = getCompletionSet("t.mtk");
|
||||||
|
|
||||||
|
std::string prefix = "t.";
|
||||||
|
CHECK(completions.size() == 2);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "mtkey1"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "mtkey2"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CompletionSet completions = getCompletionSet("t.mtkey1.");
|
||||||
|
|
||||||
|
std::string prefix = "t.mtkey1.";
|
||||||
|
CHECK(completions.size() == 2);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "x"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "y"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplFixture, "TableWithMetatableIndexFunction")
|
||||||
|
{
|
||||||
|
runCode(L, R"(
|
||||||
|
-- Create 't' which is a table with a metatable with an __index function
|
||||||
|
mt = {}
|
||||||
|
mt.__index = function(table, key)
|
||||||
|
print("mt.__index called")
|
||||||
|
if key == "foo" then
|
||||||
|
return "FOO"
|
||||||
|
elseif key == "bar" then
|
||||||
|
return "BAR"
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
t = {}
|
||||||
|
setmetatable(t, mt)
|
||||||
|
t.tkey = 0
|
||||||
|
)");
|
||||||
|
{
|
||||||
|
CompletionSet completions = getCompletionSet("t.t");
|
||||||
|
|
||||||
|
std::string prefix = "t.";
|
||||||
|
CHECK(completions.size() == 1);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "tkey"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// t.foo is a valid key, but should not be completed because it requires calling an __index function
|
||||||
|
CompletionSet completions = getCompletionSet("t.foo");
|
||||||
|
|
||||||
|
CHECK(completions.size() == 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// t.foo is a valid key, but should not be found because it requires calling an __index function
|
||||||
|
CompletionSet completions = getCompletionSet("t.foo:");
|
||||||
|
|
||||||
|
CHECK(completions.size() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplFixture, "TableWithMultipleMetatableIndexTables")
|
||||||
|
{
|
||||||
|
runCode(L, R"(
|
||||||
|
-- Create a table with a chain of metatables
|
||||||
|
mt2 = {}
|
||||||
|
mt2.__index = mt2
|
||||||
|
|
||||||
|
mt = {}
|
||||||
|
mt.__index = mt
|
||||||
|
setmetatable(mt, mt2)
|
||||||
|
|
||||||
|
t = {}
|
||||||
|
setmetatable(t, mt)
|
||||||
|
|
||||||
|
mt2.mt2key = {x=1, y=2}
|
||||||
|
mt.mtkey = 2
|
||||||
|
t.tkey = 3
|
||||||
|
)");
|
||||||
|
{
|
||||||
|
CompletionSet completions = getCompletionSet("t.");
|
||||||
|
|
||||||
|
std::string prefix = "t.";
|
||||||
|
CHECK(completions.size() == 4);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "__index"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "tkey"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "mtkey"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "mt2key"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CompletionSet completions = getCompletionSet("t.__index.");
|
||||||
|
|
||||||
|
std::string prefix = "t.__index.";
|
||||||
|
CHECK(completions.size() == 3);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "__index"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "mtkey"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "mt2key"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CompletionSet completions = getCompletionSet("t.mt2key.");
|
||||||
|
|
||||||
|
std::string prefix = "t.mt2key.";
|
||||||
|
CHECK(completions.size() == 2);
|
||||||
|
CHECK(checkCompletion(completions, prefix, "x"));
|
||||||
|
CHECK(checkCompletion(completions, prefix, "y"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplFixture, "TableWithDeepMetatableIndexTables")
|
||||||
|
{
|
||||||
|
runCode(L, R"(
|
||||||
|
-- Creates a table with a chain of metatables of length `count`
|
||||||
|
function makeChainedTable(count)
|
||||||
|
local result = {}
|
||||||
|
result.__index = result
|
||||||
|
result[string.format("entry%d", count)] = { count = count }
|
||||||
|
if count == 0 then
|
||||||
|
return result
|
||||||
|
else
|
||||||
|
return setmetatable(result, makeChainedTable(count - 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
t30 = makeChainedTable(30)
|
||||||
|
t60 = makeChainedTable(60)
|
||||||
|
)");
|
||||||
|
{
|
||||||
|
// Check if entry0 exists
|
||||||
|
CompletionSet completions = getCompletionSet("t30.entry0");
|
||||||
|
|
||||||
|
std::string prefix = "t30.";
|
||||||
|
CHECK(checkCompletion(completions, prefix, "entry0"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Check if entry0.count exists
|
||||||
|
CompletionSet completions = getCompletionSet("t30.entry0.co");
|
||||||
|
|
||||||
|
std::string prefix = "t30.entry0.";
|
||||||
|
CHECK(checkCompletion(completions, prefix, "count"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Check if entry0 exists. With the max traversal limit of 50 in the repl, this should fail.
|
||||||
|
CompletionSet completions = getCompletionSet("t60.entry0");
|
||||||
|
|
||||||
|
CHECK(completions.size() == 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Check if entry0.count exists. With the max traversal limit of 50 in the repl, this should fail.
|
||||||
|
CompletionSet completions = getCompletionSet("t60.entry0.co");
|
||||||
|
|
||||||
|
CHECK(completions.size() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/RequireTracer.h"
|
#include "Luau/RequireTracer.h"
|
||||||
|
#include "Luau/Parser.h"
|
||||||
|
|
||||||
#include "Fixture.h"
|
#include "Fixture.h"
|
||||||
|
|
||||||
|
|
|
@ -604,9 +604,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_typevars_are_not_considered_to_escape_their_
|
||||||
*/
|
*/
|
||||||
TEST_CASE_FIXTURE(Fixture, "forward_declared_alias_is_not_clobbered_by_prior_unification_with_any")
|
TEST_CASE_FIXTURE(Fixture, "forward_declared_alias_is_not_clobbered_by_prior_unification_with_any")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff[] = {
|
ScopedFastFlag sff[] = {{"LuauTwoPassAliasDefinitionFix", true}};
|
||||||
{"LuauTwoPassAliasDefinitionFix", true}
|
|
||||||
};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function x()
|
local function x()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
|
|
||||||
#include "Fixture.h"
|
#include "Fixture.h"
|
||||||
|
|
|
@ -426,8 +426,6 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_expression_singleton_options")
|
||||||
{"LuauSingletonTypes", true},
|
{"LuauSingletonTypes", true},
|
||||||
{"LuauParseSingletonTypes", true},
|
{"LuauParseSingletonTypes", true},
|
||||||
{"LuauExpectedTypesOfProperties", true},
|
{"LuauExpectedTypesOfProperties", true},
|
||||||
{"LuauIfElseExpectedType2", true},
|
|
||||||
{"LuauIfElseBranchTypeUnion", true},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
@ -2168,8 +2167,6 @@ b()
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "length_operator_union")
|
TEST_CASE_FIXTURE(Fixture, "length_operator_union")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauLengthOnCompositeType{"LuauLengthOnCompositeType", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x: {number} | {string}
|
local x: {number} | {string}
|
||||||
local y = #x
|
local y = #x
|
||||||
|
@ -2180,8 +2177,6 @@ local y = #x
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "length_operator_intersection")
|
TEST_CASE_FIXTURE(Fixture, "length_operator_intersection")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauLengthOnCompositeType{"LuauLengthOnCompositeType", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x: {number} & {z:string} -- mixed tables are evil
|
local x: {number} & {z:string} -- mixed tables are evil
|
||||||
local y = #x
|
local y = #x
|
||||||
|
@ -2192,8 +2187,6 @@ local y = #x
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "length_operator_non_table_union")
|
TEST_CASE_FIXTURE(Fixture, "length_operator_non_table_union")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauLengthOnCompositeType{"LuauLengthOnCompositeType", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x: {number} | any | string
|
local x: {number} | any | string
|
||||||
local y = #x
|
local y = #x
|
||||||
|
@ -2204,8 +2197,6 @@ local y = #x
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "length_operator_union_errors")
|
TEST_CASE_FIXTURE(Fixture, "length_operator_union_errors")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauLengthOnCompositeType{"LuauLengthOnCompositeType", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x: {number} | number | string
|
local x: {number} | number | string
|
||||||
local y = #x
|
local y = #x
|
||||||
|
@ -2214,4 +2205,38 @@ local y = #x
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "dont_hang_when_trying_to_look_up_in_cyclic_metatable_index")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{"LuauTerminateCyclicMetatableIndexLookup", true};
|
||||||
|
|
||||||
|
// t :: t1 where t1 = {metatable {__index: t1, __tostring: (t1) -> string}}
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local mt = {}
|
||||||
|
local t = setmetatable({}, mt)
|
||||||
|
mt.__index = t
|
||||||
|
|
||||||
|
function mt:__tostring()
|
||||||
|
return t.p
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK_EQ("Type 't' does not have key 'p'", toString(result.errors[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "give_up_after_one_metatable_index_look_up")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local data = { x = 5 }
|
||||||
|
local t1 = setmetatable({}, { __index = data })
|
||||||
|
local t2 = setmetatable({}, t1) -- note: must be t1, not a new table
|
||||||
|
|
||||||
|
local x1 = t1.x -- ok
|
||||||
|
local x2 = t2.x -- nope
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
CHECK_EQ("Type 't2' does not have key 'x'", toString(result.errors[0]));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "Luau/AstQuery.h"
|
#include "Luau/AstQuery.h"
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
@ -4654,8 +4653,6 @@ a = setmetatable(a, { __call = function(x) end })
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_through_group_expr")
|
TEST_CASE_FIXTURE(Fixture, "infer_through_group_expr")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauGroupExpectedType{"LuauGroupExpectedType", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function f(a: (number, number) -> number) return a(1, 3) end
|
local function f(a: (number, number) -> number) return a(1, 3) end
|
||||||
f(((function(a, b) return a + b end)))
|
f(((function(a, b) return a + b end)))
|
||||||
|
@ -4735,21 +4732,14 @@ local a = if false then "a" elseif false then "b" else "c"
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "tc_if_else_expressions_type_union")
|
TEST_CASE_FIXTURE(Fixture, "tc_if_else_expressions_type_union")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff3{"LuauIfElseBranchTypeUnion", true};
|
CheckResult result = check(R"(local a: number? = if true then 42 else nil)");
|
||||||
|
|
||||||
{
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CheckResult result = check(R"(local a: number? = if true then 42 else nil)");
|
CHECK_EQ(toString(requireType("a"), {true}), "number?");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
|
||||||
CHECK_EQ(toString(requireType("a"), {true}), "number?");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "tc_if_else_expressions_expected_type_1")
|
TEST_CASE_FIXTURE(Fixture, "tc_if_else_expressions_expected_type_1")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauIfElseExpectedType2{"LuauIfElseExpectedType2", true};
|
|
||||||
ScopedFastFlag luauIfElseBranchTypeUnion{"LuauIfElseBranchTypeUnion", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type X = {number | string}
|
type X = {number | string}
|
||||||
local a: X = if true then {"1", 2, 3} else {4, 5, 6}
|
local a: X = if true then {"1", 2, 3} else {4, 5, 6}
|
||||||
|
@ -4761,9 +4751,6 @@ local a: X = if true then {"1", 2, 3} else {4, 5, 6}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "tc_if_else_expressions_expected_type_2")
|
TEST_CASE_FIXTURE(Fixture, "tc_if_else_expressions_expected_type_2")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauIfElseExpectedType2{"LuauIfElseExpectedType2", true};
|
|
||||||
ScopedFastFlag luauIfElseBranchTypeUnion{"LuauIfElseBranchTypeUnion", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a: number? = if true then 1 else nil
|
local a: number? = if true then 1 else nil
|
||||||
)");
|
)");
|
||||||
|
@ -4773,8 +4760,6 @@ local a: number? = if true then 1 else nil
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "tc_if_else_expressions_expected_type_3")
|
TEST_CASE_FIXTURE(Fixture, "tc_if_else_expressions_expected_type_3")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauIfElseExpectedType2{"LuauIfElseExpectedType2", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function times<T>(n: any, f: () -> T)
|
local function times<T>(n: any, f: () -> T)
|
||||||
local result: {T} = {}
|
local result: {T} = {}
|
||||||
|
@ -5058,8 +5043,6 @@ end
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "recursive_metatable_crash")
|
TEST_CASE_FIXTURE(Fixture, "recursive_metatable_crash")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauMetatableAreEqualRecursion{"LuauMetatableAreEqualRecursion", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local function getIt()
|
local function getIt()
|
||||||
local y
|
local y
|
||||||
|
@ -5076,8 +5059,6 @@ local c = a or b
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "bound_typepack_promote")
|
TEST_CASE_FIXTURE(Fixture, "bound_typepack_promote")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauCommittingTxnLogFreeTpPromote{"LuauCommittingTxnLogFreeTpPromote", true};
|
|
||||||
|
|
||||||
// No assertions should trigger
|
// No assertions should trigger
|
||||||
check(R"(
|
check(R"(
|
||||||
local function p()
|
local function p()
|
||||||
|
@ -5251,7 +5232,6 @@ TEST_CASE_FIXTURE(Fixture, "taking_the_length_of_string_singleton")
|
||||||
{"LuauDiscriminableUnions2", true},
|
{"LuauDiscriminableUnions2", true},
|
||||||
{"LuauRefactorTypeVarQuestions", true},
|
{"LuauRefactorTypeVarQuestions", true},
|
||||||
{"LuauSingletonTypes", true},
|
{"LuauSingletonTypes", true},
|
||||||
{"LuauLengthOnCompositeType", true},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
@ -5272,7 +5252,6 @@ TEST_CASE_FIXTURE(Fixture, "taking_the_length_of_union_of_string_singleton")
|
||||||
{"LuauDiscriminableUnions2", true},
|
{"LuauDiscriminableUnions2", true},
|
||||||
{"LuauRefactorTypeVarQuestions", true},
|
{"LuauRefactorTypeVarQuestions", true},
|
||||||
{"LuauSingletonTypes", true},
|
{"LuauSingletonTypes", true},
|
||||||
{"LuauLengthOnCompositeType", true},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
@ -930,8 +929,6 @@ type R = { m: F<R> }
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "pack_tail_unification_check")
|
TEST_CASE_FIXTURE(Fixture, "pack_tail_unification_check")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauUnifyPackTails{"LuauUnifyPackTails", true};
|
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a: () -> (number, ...string)
|
local a: () -> (number, ...string)
|
||||||
local b: () -> (number, ...boolean)
|
local b: () -> (number, ...boolean)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Parser.h"
|
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypeVar.h"
|
#include "Luau/TypeVar.h"
|
||||||
|
|
|
@ -371,6 +371,15 @@ do
|
||||||
st, msg = coroutine.close(co)
|
st, msg = coroutine.close(co)
|
||||||
assert(st and msg == nil)
|
assert(st and msg == nil)
|
||||||
assert(f() == 42)
|
assert(f() == 42)
|
||||||
|
|
||||||
|
-- closing a coroutine with a large stack
|
||||||
|
co = coroutine.create(function()
|
||||||
|
local function f(depth) return if depth > 0 then f(depth - 1) + depth else 0 end
|
||||||
|
coroutine.yield(f(100))
|
||||||
|
end)
|
||||||
|
assert(coroutine.resume(co))
|
||||||
|
st, msg = coroutine.close(co)
|
||||||
|
assert(st and msg == nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
return 'OK'
|
return 'OK'
|
||||||
|
|
|
@ -49,16 +49,24 @@ foo()
|
||||||
c = getcoverage(foo)
|
c = getcoverage(foo)
|
||||||
assert(#c == 1)
|
assert(#c == 1)
|
||||||
assert(c[1].name == "foo")
|
assert(c[1].name == "foo")
|
||||||
|
assert(c[1].linedefined == 4)
|
||||||
|
assert(c[1].depth == 0)
|
||||||
assert(validate(c[1], {5, 6, 7}, {}))
|
assert(validate(c[1], {5, 6, 7}, {}))
|
||||||
|
|
||||||
bar()
|
bar()
|
||||||
c = getcoverage(bar)
|
c = getcoverage(bar)
|
||||||
assert(#c == 3)
|
assert(#c == 3)
|
||||||
assert(c[1].name == "bar")
|
assert(c[1].name == "bar")
|
||||||
|
assert(c[1].linedefined == 10)
|
||||||
|
assert(c[1].depth == 0)
|
||||||
assert(validate(c[1], {11, 15, 19}, {}))
|
assert(validate(c[1], {11, 15, 19}, {}))
|
||||||
assert(c[2].name == "one")
|
assert(c[2].name == "one")
|
||||||
|
assert(c[2].linedefined == 11)
|
||||||
|
assert(c[2].depth == 1)
|
||||||
assert(validate(c[2], {12}, {}))
|
assert(validate(c[2], {12}, {}))
|
||||||
assert(c[3].name == nil)
|
assert(c[3].name == nil)
|
||||||
|
assert(c[3].linedefined == 15)
|
||||||
|
assert(c[3].depth == 1)
|
||||||
assert(validate(c[3], {}, {16}))
|
assert(validate(c[3], {}, {16}))
|
||||||
|
|
||||||
return 'OK'
|
return 'OK'
|
||||||
|
|
|
@ -76,6 +76,9 @@ assert(baz(co, 2, "n") == nil)
|
||||||
assert(baz(math.sqrt, "n") == "sqrt")
|
assert(baz(math.sqrt, "n") == "sqrt")
|
||||||
assert(baz(math.sqrt, "f") == math.sqrt) -- yes this is pointless
|
assert(baz(math.sqrt, "f") == math.sqrt) -- yes this is pointless
|
||||||
|
|
||||||
|
local t = { foo = function() return 1 end }
|
||||||
|
assert(baz(t.foo, "n") == "foo")
|
||||||
|
|
||||||
-- info multi-arg returns
|
-- info multi-arg returns
|
||||||
function quux(...)
|
function quux(...)
|
||||||
return {debug.info(...)}
|
return {debug.info(...)}
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
#include <Windows.h> // IsDebuggerPresent
|
#include <Windows.h> // IsDebuggerPresent
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -52,7 +55,7 @@ static bool debuggerPresent()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int assertionHandler(const char* expr, const char* file, int line, const char* function)
|
static int testAssertionHandler(const char* expr, const char* file, int line, const char* function)
|
||||||
{
|
{
|
||||||
if (debuggerPresent())
|
if (debuggerPresent())
|
||||||
LUAU_DEBUGBREAK();
|
LUAU_DEBUGBREAK();
|
||||||
|
@ -218,7 +221,7 @@ static void setFastFlags(const std::vector<doctest::String>& flags)
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
Luau::assertHandler() = assertionHandler;
|
Luau::assertHandler() = testAssertionHandler;
|
||||||
|
|
||||||
doctest::registerReporter<BoostLikeReporter>("boost", 0, true);
|
doctest::registerReporter<BoostLikeReporter>("boost", 0, true);
|
||||||
|
|
||||||
|
|
78
tools/natvis/Analysis.natvis
Normal file
78
tools/natvis/Analysis.natvis
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
|
||||||
|
<Type Name="Luau::AnyTypeVar">
|
||||||
|
<DisplayString>AnyTypeVar</DisplayString>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="Luau::Variant<*>">
|
||||||
|
<DisplayString Condition="typeId == 0" Optional="true">{{ index=0, value={*($T1*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 1" Optional="true">{{ index=1, value={*($T2*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 2" Optional="true">{{ index=2, value={*($T3*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 3" Optional="true">{{ index=3, value={*($T4*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 4" Optional="true">{{ index=4, value={*($T5*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 5" Optional="true">{{ index=5, value={*($T6*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 6" Optional="true">{{ index=6, value={*($T7*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 7" Optional="true">{{ index=7, value={*($T8*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 8" Optional="true">{{ index=8, value={*($T9*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 9" Optional="true">{{ index=9, value={*($T10*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 10" Optional="true">{{ index=10, value={*($T11*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 11" Optional="true">{{ index=11, value={*($T12*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 12" Optional="true">{{ index=12, value={*($T13*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 13" Optional="true">{{ index=13, value={*($T14*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 14" Optional="true">{{ index=14, value={*($T15*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 15" Optional="true">{{ index=15, value={*($T16*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 16" Optional="true">{{ index=16, value={*($T17*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 17" Optional="true">{{ index=17, value={*($T18*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 18" Optional="true">{{ index=18, value={*($T19*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 19" Optional="true">{{ index=19, value={*($T20*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 20" Optional="true">{{ index=20, value={*($T21*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 21" Optional="true">{{ index=21, value={*($T22*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 22" Optional="true">{{ index=22, value={*($T23*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 23" Optional="true">{{ index=23, value={*($T24*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 24" Optional="true">{{ index=24, value={*($T25*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 25" Optional="true">{{ index=25, value={*($T26*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 26" Optional="true">{{ index=26, value={*($T27*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 27" Optional="true">{{ index=27, value={*($T28*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 28" Optional="true">{{ index=28, value={*($T29*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 29" Optional="true">{{ index=29, value={*($T30*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 30" Optional="true">{{ index=30, value={*($T31*)storage} }}</DisplayString>
|
||||||
|
<DisplayString Condition="typeId == 31" Optional="true">{{ index=31, value={*($T32*)storage} }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="index">typeId</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 0" Optional="true">*($T1*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 1" Optional="true">*($T2*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 2" Optional="true">*($T3*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 3" Optional="true">*($T4*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 4" Optional="true">*($T5*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 5" Optional="true">*($T6*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 6" Optional="true">*($T7*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 7" Optional="true">*($T8*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 8" Optional="true">*($T9*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 9" Optional="true">*($T10*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 10" Optional="true">*($T11*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 11" Optional="true">*($T12*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 12" Optional="true">*($T13*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 13" Optional="true">*($T14*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 14" Optional="true">*($T15*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 15" Optional="true">*($T16*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 16" Optional="true">*($T17*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 17" Optional="true">*($T18*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 18" Optional="true">*($T19*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 19" Optional="true">*($T20*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 20" Optional="true">*($T21*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 21" Optional="true">*($T22*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 22" Optional="true">*($T23*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 23" Optional="true">*($T24*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 24" Optional="true">*($T25*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 25" Optional="true">*($T26*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 26" Optional="true">*($T27*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 27" Optional="true">*($T28*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 28" Optional="true">*($T29*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 29" Optional="true">*($T30*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 30" Optional="true">*($T31*)storage</Item>
|
||||||
|
<Item Name="[value]" Condition="typeId == 31" Optional="true">*($T32*)storage</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
</AutoVisualizer>
|
25
tools/natvis/Ast.natvis
Normal file
25
tools/natvis/Ast.natvis
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
|
||||||
|
<Type Name="Luau::AstArray<*>">
|
||||||
|
<DisplayString>AstArray size={size}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[size]">size</Item>
|
||||||
|
<ArrayItems>
|
||||||
|
<Size>size</Size>
|
||||||
|
<ValuePointer>data</ValuePointer>
|
||||||
|
</ArrayItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="Luau::TempVector<*>">
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[size]">size_</Item>
|
||||||
|
<ArrayItems>
|
||||||
|
<Size>size_</Size>
|
||||||
|
<ValuePointer>storage._Mypair._Myval2._Myfirst + offset</ValuePointer>
|
||||||
|
</ArrayItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
</AutoVisualizer>
|
269
tools/natvis/VM.natvis
Normal file
269
tools/natvis/VM.natvis
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
|
||||||
|
<Type Name="::lua_TValue">
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TNIL">nil</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TBOOLEAN">{(bool)value.b}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TLIGHTUSERDATA">lightuserdata {value.p}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TNUMBER">number = {value.n}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TVECTOR">vector = {value.v[0]}, {value.v[1]}, {*(float*)&extra}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TSTRING">{value.gc->ts}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TTABLE">{value.gc->h}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TFUNCTION">function {value.gc->cl,view(short)}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TUSERDATA">userdata {value.gc->u}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TTHREAD">thread {value.gc->th}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TPROTO">proto {value.gc->p}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TUPVAL">upvalue {value.gc->uv}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TDEADKEY">deadkey</DisplayString>
|
||||||
|
<DisplayString>empty</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[lightuserdata]" Condition="tt == lua_Type::LUA_TLIGHTUSERDATA">value.p</Item>
|
||||||
|
<Item Name="[string]" Condition="tt == lua_Type::LUA_TSTRING">value.gc->ts</Item>
|
||||||
|
<Item Name="[table]" Condition="tt == lua_Type::LUA_TTABLE">value.gc->h</Item>
|
||||||
|
<Item Name="[C function]" Condition="tt == lua_Type::LUA_TFUNCTION && value.gc->cl.isC">value.gc->cl</Item>
|
||||||
|
<Item Name="[Lua function]" Condition="tt == lua_Type::LUA_TFUNCTION && !value.gc->cl.isC">value.gc->cl</Item>
|
||||||
|
<Item Name="[userdata]" Condition="tt == lua_Type::LUA_TUSERDATA">value.gc->u</Item>
|
||||||
|
<Item Name="[thread]" Condition="tt == lua_Type::LUA_TTHREAD">value.gc->th</Item>
|
||||||
|
<Item Name="[proto]" Condition="tt == lua_Type::LUA_TPROTO">value.gc->p</Item>
|
||||||
|
<Item Name="[upvalue]" Condition="tt == lua_Type::LUA_TUPVAL">value.gc->uv</Item>
|
||||||
|
<Synthetic Name="[gc]" Condition="tt >= lua_Type::LUA_TSTRING">
|
||||||
|
<DisplayString Condition="value.gc->gch.marked & 8">fixed ({(int)value.gc->gch.marked})</DisplayString>
|
||||||
|
<DisplayString Condition="value.gc->gch.marked & 4">black ({(int)value.gc->gch.marked})</DisplayString>
|
||||||
|
<DisplayString Condition="value.gc->gch.marked & 1">white ({(int)value.gc->gch.marked})</DisplayString>
|
||||||
|
<DisplayString Condition="value.gc->gch.marked & 2">white ({(int)value.gc->gch.marked})</DisplayString>
|
||||||
|
<DisplayString Condition="(value.gc->gch.marked & 3) == 0">gray ({(int)value.gc->gch.marked})</DisplayString>
|
||||||
|
</Synthetic>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="::TKey">
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TNIL">nil</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TBOOLEAN">{(bool)value.b}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TLIGHTUSERDATA">lightuserdata {value.p}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TNUMBER">number = {value.n}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TVECTOR">vector = {value.v[0]}, {value.v[1]}, {*(float*)&extra}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TSTRING">{value.gc->ts}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TTABLE">{value.gc->h}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TFUNCTION">function {value.gc->cl,view(short)}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TUSERDATA">userdata {value.gc->u}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TTHREAD">thread {value.gc->th}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TPROTO">proto {value.gc->p}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TUPVAL">upvalue {value.gc->uv}</DisplayString>
|
||||||
|
<DisplayString Condition="tt == lua_Type::LUA_TDEADKEY">deadkey</DisplayString>
|
||||||
|
<DisplayString>empty</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[lightuserdata]" Condition="tt == lua_Type::LUA_TLIGHTUSERDATA">(void**)value.p</Item>
|
||||||
|
<Item Name="[string]" Condition="tt == lua_Type::LUA_TSTRING">value.gc->ts</Item>
|
||||||
|
<Item Name="[table]" Condition="tt == lua_Type::LUA_TTABLE">value.gc->h</Item>
|
||||||
|
<Item Name="[C function]" Condition="tt == lua_Type::LUA_TFUNCTION && value.gc->cl.isC">value.gc->cl</Item>
|
||||||
|
<Item Name="[Lua function]" Condition="tt == lua_Type::LUA_TFUNCTION && !value.gc->cl.isC">value.gc->cl</Item>
|
||||||
|
<Item Name="[userdata]" Condition="tt == lua_Type::LUA_TUSERDATA">value.gc->u</Item>
|
||||||
|
<Item Name="[thread]" Condition="tt == lua_Type::LUA_TTHREAD">value.gc->th</Item>
|
||||||
|
<Item Name="[proto]" Condition="tt == lua_Type::LUA_TPROTO">value.gc->p</Item>
|
||||||
|
<Item Name="[upvalue]" Condition="tt == lua_Type::LUA_TUPVAL">value.gc->uv</Item>
|
||||||
|
|
||||||
|
<Item Name="[next]">next</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="::LuaNode">
|
||||||
|
<DisplayString Condition="key.tt != lua_Type::LUA_TNIL || val.tt != lua_Type::LUA_TNIL">{key,na} = {val}</DisplayString>
|
||||||
|
<DisplayString Condition="key.tt == lua_Type::LUA_TNIL && val.tt == 0">---</DisplayString>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="::Table">
|
||||||
|
<DisplayString>table</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="metatable" Condition="metatable">metatable</Item>
|
||||||
|
|
||||||
|
<Synthetic Name="hash">
|
||||||
|
<DisplayString>[size] {1<<lsizenode}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>1<<lsizenode</Size>
|
||||||
|
<ValueNode>node[$i]</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="array">
|
||||||
|
<DisplayString>[size] {sizearray}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>sizearray</Size>
|
||||||
|
<ValueNode>array[$i]</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="::Udata">
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="count" InitialValue="1<<metatable->lsizenode" />
|
||||||
|
<Variable Name="i" InitialValue="0" />
|
||||||
|
|
||||||
|
<Size>1</Size>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="i >= count" />
|
||||||
|
<If Condition="metatable->node[i].key.tt == lua_Type::LUA_TSTRING">
|
||||||
|
<If Condition="strcmp(metatable->node[i].key.value.gc->ts.data, "__type") == 0">
|
||||||
|
<Item Name="[type]">metatable->node[i].val</Item>
|
||||||
|
<Break/>
|
||||||
|
</If>
|
||||||
|
</If>
|
||||||
|
<Exec>i = i + 1</Exec>
|
||||||
|
</Loop>
|
||||||
|
<If Condition="i == count">
|
||||||
|
<Item Name="[type]">"unknown",sb</Item>
|
||||||
|
</If>
|
||||||
|
</CustomListItems>
|
||||||
|
<Item Name="tag">tag</Item>
|
||||||
|
<Item Name="len">len</Item>
|
||||||
|
<Item Name="metatable">metatable</Item>
|
||||||
|
<Item Name="data">data</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="::Closure">
|
||||||
|
<DisplayString Condition="isC == 1" IncludeView="short">{c.f,na}</DisplayString>
|
||||||
|
<DisplayString Condition="isC == 0" IncludeView="short">{l.p,na}</DisplayString>
|
||||||
|
<DisplayString Condition="isC == 1" ExcludeView="short">{c}</DisplayString>
|
||||||
|
<DisplayString Condition="isC == 0" ExcludeView="short">{l}</DisplayString>
|
||||||
|
<DisplayString>invalid</DisplayString>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="::TString">
|
||||||
|
<DisplayString>{data,s}</DisplayString>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name ="::lua_State">
|
||||||
|
<DisplayString Condition="ci->func->value.gc->cl.isC">
|
||||||
|
{ci->func->value.gc->cl.c.f,na}
|
||||||
|
</DisplayString>
|
||||||
|
<DisplayString Condition="!ci->func->value.gc->cl.isC && ci->func->value.gc->cl.l.p->debugname" Optional="true">
|
||||||
|
{ci->func->value.gc->cl.l.p->source->data,sb}:{ci->func->value.gc->cl.l.p->linedefined,d} {ci->func->value.gc->cl.l.p->debugname->data,sb}
|
||||||
|
</DisplayString>
|
||||||
|
<DisplayString Condition="!ci->func->value.gc->cl.isC" Optional="true">
|
||||||
|
{ci->func->value.gc->cl.l.p->source->data,sb}:{ci->func->value.gc->cl.l.p->linedefined,d}
|
||||||
|
</DisplayString>
|
||||||
|
<DisplayString>thread</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Synthetic Name="[call stack]">
|
||||||
|
<DisplayString>{ci-base_ci} frames</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>ci-base_ci</Size>
|
||||||
|
<!-- the +1 is omitted here to avoid some issues with a blank call -->
|
||||||
|
<ValueNode>
|
||||||
|
base_ci[ci-base_ci - $i].func->value.gc->cl,view(short)
|
||||||
|
</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[top frame stack]">
|
||||||
|
<DisplayString>{top-base} values</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ArrayItems>
|
||||||
|
<Size>top-base</Size>
|
||||||
|
<ValuePointer>base</ValuePointer>
|
||||||
|
</ArrayItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[stack]">
|
||||||
|
<DisplayString>{top-stack} values</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ArrayItems>
|
||||||
|
<Size>top-stack</Size>
|
||||||
|
<ValuePointer>stack</ValuePointer>
|
||||||
|
</ArrayItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[open upvals]" Condition="openupval != 0">
|
||||||
|
<Expand>
|
||||||
|
<LinkedListItems>
|
||||||
|
<HeadPointer>openupval</HeadPointer>
|
||||||
|
<NextPointer>u.l.next</NextPointer>
|
||||||
|
<ValueNode>this</ValueNode>
|
||||||
|
</LinkedListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Item Name="l_gt">l_gt</Item>
|
||||||
|
<Item Name="env">env</Item>
|
||||||
|
<Item Name="userdata">userdata</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="::Proto">
|
||||||
|
<DisplayString Condition="debugname">{source->data,sb}:{linedefined} function {debugname->data,sb} [{(int)numparams} arg, {(int)nups} upval]</DisplayString>
|
||||||
|
<DisplayString Condition="!debugname">{source->data,sb}:{linedefined} [{(int)numparams} arg, {(int)nups} upval]</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="debugname">debugname</Item>
|
||||||
|
<Synthetic Name="[constants]">
|
||||||
|
<DisplayString>constants</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>sizek</Size>
|
||||||
|
<ValueNode>k[$i]</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[locals]">
|
||||||
|
<DisplayString>locals</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>sizelocvars</Size>
|
||||||
|
<ValueNode>locvars[$i]</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[bytecode]">
|
||||||
|
<DisplayString>bytecode</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>sizecode</Size>
|
||||||
|
<ValueNode>code[$i]</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[functions]">
|
||||||
|
<DisplayString>functions</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>sizep</Size>
|
||||||
|
<ValueNode>p[$i]</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[upvals]">
|
||||||
|
<DisplayString>upvals</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>sizeupvalues</Size>
|
||||||
|
<ValueNode>upvalues[$i]</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
|
||||||
|
<Item Name="source">source</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="::GCheader">
|
||||||
|
<Expand>
|
||||||
|
<Synthetic Name="[type]">
|
||||||
|
<DisplayString>{(lua_Type)tt}</DisplayString>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[gc]">
|
||||||
|
<DisplayString Condition="marked & 8">fixed ({(int)marked})</DisplayString>
|
||||||
|
<DisplayString Condition="marked & 4">black ({(int)marked})</DisplayString>
|
||||||
|
<DisplayString Condition="marked & 1">white ({(int)marked})</DisplayString>
|
||||||
|
<DisplayString Condition="marked & 2">white ({(int)marked})</DisplayString>
|
||||||
|
<DisplayString Condition="(marked & 3) == 0">gray ({(int)marked})</DisplayString>
|
||||||
|
<DisplayString>unknown</DisplayString>
|
||||||
|
</Synthetic>
|
||||||
|
<Item Name="memcat">memcat</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
</AutoVisualizer>
|
Loading…
Reference in a new issue