Merge branch 'upstream' into merge

This commit is contained in:
Arseny Kapoulkine 2022-07-21 13:37:02 -07:00
commit 2c0be898a1
41 changed files with 1484 additions and 285 deletions

View File

@ -2,12 +2,15 @@
#pragma once #pragma once
#include <string> #include <string>
#include <vector>
namespace Luau namespace Luau
{ {
class AstNode; class AstNode;
struct Comment;
std::string toJson(AstNode* node); std::string toJson(AstNode* node);
std::string toJson(AstNode* node, const std::vector<Comment>& commentLocations);
} // namespace Luau } // namespace Luau

View File

@ -10,6 +10,7 @@
LUAU_FASTINT(LuauVisitRecursionLimit) LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative) LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
LUAU_FASTFLAG(LuauCompleteVisitor);
namespace Luau namespace Luau
{ {
@ -129,11 +130,11 @@ struct GenericTypeVarVisitor
{ {
return visit(ty); return visit(ty);
} }
virtual bool visit(TypeId ty, const UnknownTypeVar& atv) virtual bool visit(TypeId ty, const UnknownTypeVar& utv)
{ {
return visit(ty); return visit(ty);
} }
virtual bool visit(TypeId ty, const NeverTypeVar& atv) virtual bool visit(TypeId ty, const NeverTypeVar& ntv)
{ {
return visit(ty); return visit(ty);
} }
@ -145,6 +146,14 @@ struct GenericTypeVarVisitor
{ {
return visit(ty); return visit(ty);
} }
virtual bool visit(TypeId ty, const BlockedTypeVar& btv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const SingletonTypeVar& stv)
{
return visit(ty);
}
virtual bool visit(TypePackId tp) virtual bool visit(TypePackId tp)
{ {
@ -190,16 +199,12 @@ struct GenericTypeVarVisitor
if (visit(ty, *btv)) if (visit(ty, *btv))
traverse(btv->boundTo); traverse(btv->boundTo);
} }
else if (auto ftv = get<FreeTypeVar>(ty)) else if (auto ftv = get<FreeTypeVar>(ty))
visit(ty, *ftv); visit(ty, *ftv);
else if (auto gtv = get<GenericTypeVar>(ty)) else if (auto gtv = get<GenericTypeVar>(ty))
visit(ty, *gtv); visit(ty, *gtv);
else if (auto etv = get<ErrorTypeVar>(ty)) else if (auto etv = get<ErrorTypeVar>(ty))
visit(ty, *etv); visit(ty, *etv);
else if (auto ctv = get<ConstrainedTypeVar>(ty)) else if (auto ctv = get<ConstrainedTypeVar>(ty))
{ {
if (visit(ty, *ctv)) if (visit(ty, *ctv))
@ -208,10 +213,8 @@ struct GenericTypeVarVisitor
traverse(part); traverse(part);
} }
} }
else if (auto ptv = get<PrimitiveTypeVar>(ty)) else if (auto ptv = get<PrimitiveTypeVar>(ty))
visit(ty, *ptv); visit(ty, *ptv);
else if (auto ftv = get<FunctionTypeVar>(ty)) else if (auto ftv = get<FunctionTypeVar>(ty))
{ {
if (visit(ty, *ftv)) if (visit(ty, *ftv))
@ -220,7 +223,6 @@ struct GenericTypeVarVisitor
traverse(ftv->retTypes); traverse(ftv->retTypes);
} }
} }
else if (auto ttv = get<TableTypeVar>(ty)) else if (auto ttv = get<TableTypeVar>(ty))
{ {
// Some visitors want to see bound tables, that's why we traverse the original type // Some visitors want to see bound tables, that's why we traverse the original type
@ -243,7 +245,6 @@ struct GenericTypeVarVisitor
} }
} }
} }
else if (auto mtv = get<MetatableTypeVar>(ty)) else if (auto mtv = get<MetatableTypeVar>(ty))
{ {
if (visit(ty, *mtv)) if (visit(ty, *mtv))
@ -252,7 +253,6 @@ struct GenericTypeVarVisitor
traverse(mtv->metatable); traverse(mtv->metatable);
} }
} }
else if (auto ctv = get<ClassTypeVar>(ty)) else if (auto ctv = get<ClassTypeVar>(ty))
{ {
if (visit(ty, *ctv)) if (visit(ty, *ctv))
@ -267,10 +267,8 @@ struct GenericTypeVarVisitor
traverse(*ctv->metatable); traverse(*ctv->metatable);
} }
} }
else if (auto atv = get<AnyTypeVar>(ty)) else if (auto atv = get<AnyTypeVar>(ty))
visit(ty, *atv); visit(ty, *atv);
else if (auto utv = get<UnionTypeVar>(ty)) else if (auto utv = get<UnionTypeVar>(ty))
{ {
if (visit(ty, *utv)) if (visit(ty, *utv))
@ -279,7 +277,6 @@ struct GenericTypeVarVisitor
traverse(optTy); traverse(optTy);
} }
} }
else if (auto itv = get<IntersectionTypeVar>(ty)) else if (auto itv = get<IntersectionTypeVar>(ty))
{ {
if (visit(ty, *itv)) if (visit(ty, *itv))
@ -288,6 +285,24 @@ struct GenericTypeVarVisitor
traverse(partTy); traverse(partTy);
} }
} }
else if (!FFlag::LuauCompleteVisitor)
return visit_detail::unsee(seen, ty);
else if (get<LazyTypeVar>(ty))
{
// Visiting into LazyTypeVar may necessarily cause infinite expansion, so we don't do that on purpose.
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ClassTypeVar
// that doesn't need to be expanded.
}
else if (auto stv = get<SingletonTypeVar>(ty))
visit(ty, *stv);
else if (auto btv = get<BlockedTypeVar>(ty))
visit(ty, *btv);
else if (auto utv = get<UnknownTypeVar>(ty))
visit(ty, *utv);
else if (auto ntv = get<NeverTypeVar>(ty))
visit(ty, *ntv);
else
LUAU_ASSERT(!"GenericTypeVarVisitor::traverse(TypeId) is not exhaustive!");
visit_detail::unsee(seen, ty); visit_detail::unsee(seen, ty);
} }

View File

@ -2,6 +2,7 @@
#include "Luau/JsonEncoder.h" #include "Luau/JsonEncoder.h"
#include "Luau/Ast.h" #include "Luau/Ast.h"
#include "Luau/ParseResult.h"
#include "Luau/StringUtils.h" #include "Luau/StringUtils.h"
#include "Luau/Common.h" #include "Luau/Common.h"
@ -75,6 +76,11 @@ struct AstJsonEncoder : public AstVisitor
writeRaw(std::string_view{&c, 1}); writeRaw(std::string_view{&c, 1});
} }
void writeType(std::string_view propValue)
{
write("type", propValue);
}
template<typename T> template<typename T>
void write(std::string_view propName, const T& value) void write(std::string_view propName, const T& value)
{ {
@ -98,7 +104,7 @@ struct AstJsonEncoder : public AstVisitor
void write(double d) void write(double d)
{ {
char b[256]; char b[256];
sprintf(b, "%g", d); sprintf(b, "%.17g", d);
writeRaw(b); writeRaw(b);
} }
@ -111,8 +117,12 @@ struct AstJsonEncoder : public AstVisitor
{ {
if (c == '"') if (c == '"')
writeRaw("\\\""); writeRaw("\\\"");
else if (c == '\0') else if (c == '\\')
writeRaw("\\\0"); writeRaw("\\\\");
else if (c < ' ')
writeRaw(format("\\u%04x", c));
else if (c == '\n')
writeRaw("\\n");
else else
writeRaw(c); writeRaw(c);
} }
@ -189,10 +199,11 @@ struct AstJsonEncoder : public AstVisitor
writeRaw("{"); writeRaw("{");
bool c = pushComma(); bool c = pushComma();
if (local->annotation != nullptr) if (local->annotation != nullptr)
write("type", local->annotation); write("luauType", local->annotation);
else else
write("type", nullptr); write("luauType", nullptr);
write("name", local->name); write("name", local->name);
writeType("AstLocal");
write("location", local->location); write("location", local->location);
popComma(c); popComma(c);
writeRaw("}"); writeRaw("}");
@ -208,7 +219,7 @@ struct AstJsonEncoder : public AstVisitor
{ {
writeRaw("{"); writeRaw("{");
bool c = pushComma(); bool c = pushComma();
write("type", name); writeType(name);
writeNode(node); writeNode(node);
f(); f();
popComma(c); popComma(c);
@ -358,6 +369,7 @@ struct AstJsonEncoder : public AstVisitor
{ {
writeRaw("{"); writeRaw("{");
bool c = pushComma(); bool c = pushComma();
writeType("AstTypeList");
write("types", typeList.types); write("types", typeList.types);
if (typeList.tailType) if (typeList.tailType)
write("tailType", typeList.tailType); write("tailType", typeList.tailType);
@ -369,9 +381,10 @@ struct AstJsonEncoder : public AstVisitor
{ {
writeRaw("{"); writeRaw("{");
bool c = pushComma(); bool c = pushComma();
writeType("AstGenericType");
write("name", genericType.name); write("name", genericType.name);
if (genericType.defaultValue) if (genericType.defaultValue)
write("type", genericType.defaultValue); write("luauType", genericType.defaultValue);
popComma(c); popComma(c);
writeRaw("}"); writeRaw("}");
} }
@ -380,9 +393,10 @@ struct AstJsonEncoder : public AstVisitor
{ {
writeRaw("{"); writeRaw("{");
bool c = pushComma(); bool c = pushComma();
writeType("AstGenericTypePack");
write("name", genericTypePack.name); write("name", genericTypePack.name);
if (genericTypePack.defaultValue) if (genericTypePack.defaultValue)
write("type", genericTypePack.defaultValue); write("luauType", genericTypePack.defaultValue);
popComma(c); popComma(c);
writeRaw("}"); writeRaw("}");
} }
@ -404,6 +418,7 @@ struct AstJsonEncoder : public AstVisitor
{ {
writeRaw("{"); writeRaw("{");
bool c = pushComma(); bool c = pushComma();
writeType("AstExprTableItem");
write("kind", item.kind); write("kind", item.kind);
switch (item.kind) switch (item.kind)
{ {
@ -419,6 +434,17 @@ struct AstJsonEncoder : public AstVisitor
writeRaw("}"); writeRaw("}");
} }
void write(class AstExprIfElse* node)
{
writeNode(node, "AstExprIfElse", [&]() {
PROP(condition);
PROP(hasThen);
PROP(trueExpr);
PROP(hasElse);
PROP(falseExpr);
});
}
void write(class AstExprTable* node) void write(class AstExprTable* node)
{ {
writeNode(node, "AstExprTable", [&]() { writeNode(node, "AstExprTable", [&]() {
@ -431,11 +457,11 @@ struct AstJsonEncoder : public AstVisitor
switch (op) switch (op)
{ {
case AstExprUnary::Not: case AstExprUnary::Not:
return writeString("not"); return writeString("Not");
case AstExprUnary::Minus: case AstExprUnary::Minus:
return writeString("minus"); return writeString("Minus");
case AstExprUnary::Len: case AstExprUnary::Len:
return writeString("len"); return writeString("Len");
} }
} }
@ -541,7 +567,7 @@ struct AstJsonEncoder : public AstVisitor
void write(class AstStatWhile* node) void write(class AstStatWhile* node)
{ {
writeNode(node, "AtStatWhile", [&]() { writeNode(node, "AstStatWhile", [&]() {
PROP(condition); PROP(condition);
PROP(body); PROP(body);
PROP(hasDo); PROP(hasDo);
@ -684,7 +710,8 @@ struct AstJsonEncoder : public AstVisitor
writeRaw("{"); writeRaw("{");
bool c = pushComma(); bool c = pushComma();
write("name", prop.name); write("name", prop.name);
write("type", prop.ty); writeType("AstDeclaredClassProp");
write("luauType", prop.ty);
popComma(c); popComma(c);
writeRaw("}"); writeRaw("}");
} }
@ -731,8 +758,9 @@ struct AstJsonEncoder : public AstVisitor
bool c = pushComma(); bool c = pushComma();
write("name", prop.name); write("name", prop.name);
writeType("AstTableProp");
write("location", prop.location); write("location", prop.location);
write("type", prop.type); write("propType", prop.type);
popComma(c); popComma(c);
writeRaw("}"); writeRaw("}");
@ -745,6 +773,24 @@ struct AstJsonEncoder : public AstVisitor
PROP(indexer); PROP(indexer);
}); });
} }
void write(struct AstTableIndexer* indexer)
{
if (indexer)
{
writeRaw("{");
bool c = pushComma();
write("location", indexer->location);
write("indexType", indexer->indexType);
write("resultType", indexer->resultType);
popComma(c);
writeRaw("}");
}
else
{
writeRaw("null");
}
}
void write(class AstTypeFunction* node) void write(class AstTypeFunction* node)
{ {
@ -836,6 +882,12 @@ struct AstJsonEncoder : public AstVisitor
return false; return false;
} }
bool visit(class AstExprIfElse* node) override
{
write(node);
return false;
}
bool visit(class AstExprLocal* node) override bool visit(class AstExprLocal* node) override
{ {
write(node); write(node);
@ -1093,6 +1145,42 @@ struct AstJsonEncoder : public AstVisitor
write(node); write(node);
return false; return false;
} }
void writeComments(std::vector<Comment> commentLocations)
{
bool commentComma = false;
for (Comment comment : commentLocations)
{
if (commentComma)
{
writeRaw(",");
}
else
{
commentComma = true;
}
writeRaw("{");
bool c = pushComma();
switch (comment.type)
{
case Lexeme::Comment:
writeType("Comment");
break;
case Lexeme::BlockComment:
writeType("BlockComment");
break;
case Lexeme::BrokenComment:
writeType("BrokenComment");
break;
default:
break;
}
write("location", comment.location);
popComma(c);
writeRaw("}");
}
}
}; };
std::string toJson(AstNode* node) std::string toJson(AstNode* node)
@ -1102,4 +1190,15 @@ std::string toJson(AstNode* node)
return encoder.str(); return encoder.str();
} }
std::string toJson(AstNode* node, const std::vector<Comment>& commentLocations)
{
AstJsonEncoder encoder;
encoder.writeRaw(R"({"root":)");
node->visit(&encoder);
encoder.writeRaw(R"(,"commentLocations":[)");
encoder.writeComments(commentLocations);
encoder.writeRaw("]}");
return encoder.str();
}
} // namespace Luau } // namespace Luau

View File

@ -17,6 +17,7 @@
LUAU_FASTFLAG(LuauLowerBoundsCalculation); LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative); LUAU_FASTFLAG(LuauNormalizeFlagIsConservative);
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauForceExportSurfacesToBeNormal, false);
namespace Luau namespace Luau
{ {
@ -124,15 +125,21 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
moduleScope2->returnType = returnType; // TODO varargPack moduleScope2->returnType = returnType; // TODO varargPack
} }
ForceNormal forceNormal{&interfaceTypes};
if (FFlag::LuauLowerBoundsCalculation) if (FFlag::LuauLowerBoundsCalculation)
{ {
normalize(returnType, interfaceTypes, ice); normalize(returnType, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(returnType);
if (varargPack) if (varargPack)
{
normalize(*varargPack, interfaceTypes, ice); normalize(*varargPack, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(*varargPack);
}
} }
ForceNormal forceNormal{&interfaceTypes};
if (exportedTypeBindings) if (exportedTypeBindings)
{ {
for (auto& [name, tf] : *exportedTypeBindings) for (auto& [name, tf] : *exportedTypeBindings)
@ -147,6 +154,16 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
// We're about to freeze the memory. We know that the flag is conservative by design. Cyclic tables // We're about to freeze the memory. We know that the flag is conservative by design. Cyclic tables
// won't be marked normal. If the types aren't normal by now, they never will be. // won't be marked normal. If the types aren't normal by now, they never will be.
forceNormal.traverse(tf.type); forceNormal.traverse(tf.type);
for (GenericTypeDefinition param : tf.typeParams)
{
forceNormal.traverse(param.ty);
if (param.defaultValue)
{
normalize(*param.defaultValue, interfaceTypes, ice);
forceNormal.traverse(*param.defaultValue);
}
}
} }
} }
} }
@ -166,7 +183,12 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
{ {
ty = clone(ty, interfaceTypes, cloneState); ty = clone(ty, interfaceTypes, cloneState);
if (FFlag::LuauLowerBoundsCalculation) if (FFlag::LuauLowerBoundsCalculation)
{
normalize(ty, interfaceTypes, ice); normalize(ty, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(ty);
}
} }
freeze(internalTypes); freeze(internalTypes);

View File

@ -31,6 +31,7 @@ LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 300)
LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500) LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
LUAU_FASTFLAG(LuauKnowsTheDataModel3) LUAU_FASTFLAG(LuauKnowsTheDataModel3)
LUAU_FASTFLAG(LuauAutocompleteDynamicLimits) LUAU_FASTFLAG(LuauAutocompleteDynamicLimits)
LUAU_FASTFLAGVARIABLE(LuauExpectedTableUnionIndexerType, false)
LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false) LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false)
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false) LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
@ -38,7 +39,6 @@ LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix2, false)
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false) LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false. LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative) LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
LUAU_FASTFLAGVARIABLE(LuauReturnTypeInferenceInNonstrict, false)
LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false); LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false);
LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false); LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false);
LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false) LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
@ -50,6 +50,7 @@ LUAU_FASTFLAGVARIABLE(LuauCheckGenericHOFTypes, false)
LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false) LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false)
LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false) LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false)
LUAU_FASTFLAGVARIABLE(LuauReturnsFromCallsitesAreNotWidened, false) LUAU_FASTFLAGVARIABLE(LuauReturnsFromCallsitesAreNotWidened, false)
LUAU_FASTFLAGVARIABLE(LuauCompleteVisitor, false)
namespace Luau namespace Luau
{ {
@ -890,7 +891,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatReturn& return_)
TypePackId retPack = checkExprList(scope, return_.location, return_.list, false, {}, expectedTypes).type; TypePackId retPack = checkExprList(scope, return_.location, return_.list, false, {}, expectedTypes).type;
if (FFlag::LuauReturnTypeInferenceInNonstrict ? FFlag::LuauLowerBoundsCalculation : useConstrainedIntersections()) if (useConstrainedIntersections())
{ {
unifyLowerBound(retPack, scope->returnType, demoter.demotedLevel(scope->level), return_.location); unifyLowerBound(retPack, scope->returnType, demoter.demotedLevel(scope->level), return_.location);
return; return;
@ -1292,6 +1293,11 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
for (size_t i = 2; i < varTypes.size(); ++i) for (size_t i = 2; i < varTypes.size(); ++i)
unify(nilType, varTypes[i], forin.location); unify(nilType, varTypes[i], forin.location);
} }
else if (isNonstrictMode())
{
for (TypeId var : varTypes)
unify(anyType, var, forin.location);
}
else else
{ {
TypeId varTy = errorRecoveryType(loopScope); TypeId varTy = errorRecoveryType(loopScope);
@ -1385,12 +1391,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
// If in nonstrict mode and allowing redefinition of global function, restore the previous definition type // If in nonstrict mode and allowing redefinition of global function, restore the previous definition type
// in case this function has a differing signature. The signature discrepancy will be caught in checkBlock. // in case this function has a differing signature. The signature discrepancy will be caught in checkBlock.
if (previouslyDefined) if (previouslyDefined)
{
if (FFlag::LuauReturnTypeInferenceInNonstrict && FFlag::LuauLowerBoundsCalculation)
quantify(funScope, ty, exprName->location);
globalBindings[name] = oldBinding; globalBindings[name] = oldBinding;
}
else else
globalBindings[name] = {quantify(funScope, ty, exprName->location), exprName->location}; globalBindings[name] = {quantify(funScope, ty, exprName->location), exprName->location};
@ -2365,9 +2366,16 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
{ {
std::vector<TypeId> expectedResultTypes; std::vector<TypeId> expectedResultTypes;
for (TypeId expectedOption : expectedUnion) for (TypeId expectedOption : expectedUnion)
{
if (const TableTypeVar* ttv = get<TableTypeVar>(follow(expectedOption))) if (const TableTypeVar* ttv = get<TableTypeVar>(follow(expectedOption)))
{
if (auto prop = ttv->props.find(key->value.data); prop != ttv->props.end()) if (auto prop = ttv->props.find(key->value.data); prop != ttv->props.end())
expectedResultTypes.push_back(prop->second.type); expectedResultTypes.push_back(prop->second.type);
else if (FFlag::LuauExpectedTableUnionIndexerType && ttv->indexer && maybeString(ttv->indexer->indexType))
expectedResultTypes.push_back(ttv->indexer->indexResultType);
}
}
if (expectedResultTypes.size() == 1) if (expectedResultTypes.size() == 1)
expectedResultType = expectedResultTypes[0]; expectedResultType = expectedResultTypes[0];
else if (expectedResultTypes.size() > 1) else if (expectedResultTypes.size() > 1)
@ -3367,7 +3375,7 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(const ScopePtr&
TypePackId retPack; TypePackId retPack;
if (expr.returnAnnotation) if (expr.returnAnnotation)
retPack = resolveTypePack(funScope, *expr.returnAnnotation); retPack = resolveTypePack(funScope, *expr.returnAnnotation);
else if (FFlag::LuauReturnTypeInferenceInNonstrict ? (!FFlag::LuauLowerBoundsCalculation && isNonstrictMode()) : isNonstrictMode()) else if (isNonstrictMode())
retPack = anyTypePack; retPack = anyTypePack;
else if (expectedFunctionType && else if (expectedFunctionType &&
(!FFlag::LuauCheckGenericHOFTypes || (expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty()))) (!FFlag::LuauCheckGenericHOFTypes || (expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty())))

View File

@ -7,6 +7,7 @@
#include "Luau/Transpiler.h" #include "Luau/Transpiler.h"
#include "FileUtils.h" #include "FileUtils.h"
#include "Flags.h"
#ifdef CALLGRIND #ifdef CALLGRIND
#include <valgrind/callgrind.h> #include <valgrind/callgrind.h>
@ -223,9 +224,7 @@ int main(int argc, char** argv)
{ {
Luau::assertHandler() = assertionHandler; Luau::assertHandler() = assertionHandler;
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next) setLuauFlagsDefault();
if (strncmp(flag->name, "Luau", 4) == 0)
flag->value = true;
if (argc >= 2 && strcmp(argv[1], "--help") == 0) if (argc >= 2 && strcmp(argv[1], "--help") == 0)
{ {
@ -252,12 +251,14 @@ int main(int argc, char** argv)
annotate = true; annotate = true;
else if (strcmp(argv[i], "--timetrace") == 0) else if (strcmp(argv[i], "--timetrace") == 0)
FFlag::DebugLuauTimeTracing.value = true; FFlag::DebugLuauTimeTracing.value = true;
else if (strncmp(argv[i], "--fflags=", 9) == 0)
setLuauFlags(argv[i] + 9);
} }
#if !defined(LUAU_ENABLE_TIME_TRACE) #if !defined(LUAU_ENABLE_TIME_TRACE)
if (FFlag::DebugLuauTimeTracing) if (FFlag::DebugLuauTimeTracing)
{ {
printf("To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n"); fprintf(stderr, "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
return 1; return 1;
} }
#endif #endif

View File

@ -62,6 +62,7 @@ int main(int argc, char** argv)
Luau::AstNameTable names(allocator); Luau::AstNameTable names(allocator);
Luau::ParseOptions options; Luau::ParseOptions options;
options.captureComments = true;
options.supportContinueStatement = true; options.supportContinueStatement = true;
options.allowTypeAnnotations = true; options.allowTypeAnnotations = true;
options.allowDeclarationSyntax = true; options.allowDeclarationSyntax = true;
@ -78,7 +79,7 @@ int main(int argc, char** argv)
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
printf("%s", Luau::toJson(parseResult.root).c_str()); printf("%s", Luau::toJson(parseResult.root, parseResult.commentLocations).c_str());
return parseResult.errors.size() > 0 ? 1 : 0; return parseResult.errors.size() > 0 ? 1 : 0;
} }

75
CLI/Flags.cpp Normal file
View File

@ -0,0 +1,75 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Common.h"
#include "Luau/ExperimentalFlags.h"
#include <string_view>
#include <stdio.h>
#include <string.h>
static void setLuauFlag(std::string_view name, bool state)
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
{
if (name == flag->name)
{
flag->value = state;
return;
}
}
fprintf(stderr, "Warning: unrecognized flag '%.*s'.\n", int(name.length()), name.data());
}
static void setLuauFlags(bool state)
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "Luau", 4) == 0)
flag->value = state;
}
void setLuauFlagsDefault()
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "Luau", 4) == 0 && !Luau::isFlagExperimental(flag->name))
flag->value = true;
}
void setLuauFlags(const char* list)
{
std::string_view rest = list;
while (!rest.empty())
{
size_t ending = rest.find(",");
std::string_view element = rest.substr(0, ending);
if (size_t separator = element.find('='); separator != std::string_view::npos)
{
std::string_view key = element.substr(0, separator);
std::string_view value = element.substr(separator + 1);
if (value == "true" || value == "True")
setLuauFlag(key, true);
else if (value == "false" || value == "False")
setLuauFlag(key, false);
else
fprintf(stderr, "Warning: unrecognized value '%.*s' for flag '%.*s'.\n", int(value.length()), value.data(), int(key.length()),
key.data());
}
else
{
if (element == "true" || element == "True")
setLuauFlags(true);
else if (element == "false" || element == "False")
setLuauFlags(false);
else
setLuauFlag(element, true);
}
if (ending != std::string_view::npos)
rest.remove_prefix(ending + 1);
else
break;
}
}

5
CLI/Flags.h Normal file
View File

@ -0,0 +1,5 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
void setLuauFlagsDefault();
void setLuauFlags(const char* list);

View File

@ -8,9 +8,10 @@
#include "Luau/BytecodeBuilder.h" #include "Luau/BytecodeBuilder.h"
#include "Luau/Parser.h" #include "Luau/Parser.h"
#include "FileUtils.h"
#include "Profiler.h"
#include "Coverage.h" #include "Coverage.h"
#include "FileUtils.h"
#include "Flags.h"
#include "Profiler.h"
#include "isocline.h" #include "isocline.h"
@ -97,7 +98,11 @@ static int lua_require(lua_State* L)
// return the module from the cache // return the module from the cache
lua_getfield(L, -1, name.c_str()); lua_getfield(L, -1, name.c_str());
if (!lua_isnil(L, -1)) if (!lua_isnil(L, -1))
{
// L stack: _MODULES result
return finishrequire(L); return finishrequire(L);
}
lua_pop(L, 1); lua_pop(L, 1);
std::optional<std::string> source = readFile(name + ".luau"); std::optional<std::string> source = readFile(name + ".luau");
@ -109,6 +114,7 @@ static int lua_require(lua_State* L)
} }
// module needs to run in a new thread, isolated from the rest // module needs to run in a new thread, isolated from the rest
// note: we create ML on main thread so that it doesn't inherit environment of L
lua_State* GL = lua_mainthread(L); lua_State* GL = lua_mainthread(L);
lua_State* ML = lua_newthread(GL); lua_State* ML = lua_newthread(GL);
lua_xmove(GL, L, 1); lua_xmove(GL, L, 1);
@ -142,11 +148,12 @@ static int lua_require(lua_State* L)
} }
} }
// there's now a return value on top of ML; stack of L is MODULES thread // there's now a return value on top of ML; L stack: _MODULES ML
lua_xmove(ML, L, 1); lua_xmove(ML, L, 1);
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setfield(L, -4, name.c_str()); lua_setfield(L, -4, name.c_str());
// L stack: _MODULES ML result
return finishrequire(L); return finishrequire(L);
} }
@ -682,60 +689,11 @@ static int assertionHandler(const char* expr, const char* file, int line, const
return 1; return 1;
} }
static void setLuauFlags(bool state)
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
{
if (strncmp(flag->name, "Luau", 4) == 0)
flag->value = state;
}
}
static void setFlag(std::string_view name, bool state)
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
{
if (name == flag->name)
{
flag->value = state;
return;
}
}
fprintf(stderr, "Warning: --fflag unrecognized flag '%.*s'.\n\n", int(name.length()), name.data());
}
static void applyFlagKeyValue(std::string_view element)
{
if (size_t separator = element.find('='); separator != std::string_view::npos)
{
std::string_view key = element.substr(0, separator);
std::string_view value = element.substr(separator + 1);
if (value == "true")
setFlag(key, true);
else if (value == "false")
setFlag(key, false);
else
fprintf(stderr, "Warning: --fflag unrecognized value '%.*s' for flag '%.*s'.\n\n", int(value.length()), value.data(), int(key.length()),
key.data());
}
else
{
if (element == "true")
setLuauFlags(true);
else if (element == "false")
setLuauFlags(false);
else
setFlag(element, true);
}
}
int replMain(int argc, char** argv) int replMain(int argc, char** argv)
{ {
Luau::assertHandler() = assertionHandler; Luau::assertHandler() = assertionHandler;
setLuauFlags(true); setLuauFlagsDefault();
CliMode mode = CliMode::Unknown; CliMode mode = CliMode::Unknown;
CompileFormat compileFormat{}; CompileFormat compileFormat{};
@ -818,27 +776,10 @@ int replMain(int argc, char** argv)
else if (strcmp(argv[i], "--timetrace") == 0) else if (strcmp(argv[i], "--timetrace") == 0)
{ {
FFlag::DebugLuauTimeTracing.value = true; FFlag::DebugLuauTimeTracing.value = true;
#if !defined(LUAU_ENABLE_TIME_TRACE)
printf("To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
return 1;
#endif
} }
else if (strncmp(argv[i], "--fflags=", 9) == 0) else if (strncmp(argv[i], "--fflags=", 9) == 0)
{ {
std::string_view list = argv[i] + 9; setLuauFlags(argv[i] + 9);
while (!list.empty())
{
size_t ending = list.find(",");
applyFlagKeyValue(list.substr(0, ending));
if (ending != std::string_view::npos)
list.remove_prefix(ending + 1);
else
break;
}
} }
else if (argv[i][0] == '-') else if (argv[i][0] == '-')
{ {
@ -848,6 +789,14 @@ int replMain(int argc, char** argv)
} }
} }
#if !defined(LUAU_ENABLE_TIME_TRACE)
if (FFlag::DebugLuauTimeTracing)
{
fprintf(stderr, "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
return 1;
}
#endif
const std::vector<std::string> files = getSourceFiles(argc, argv); const std::vector<std::string> files = getSourceFiles(argc, argv);
if (mode == CliMode::Unknown) if (mode == CliMode::Unknown)
{ {

View File

@ -61,12 +61,22 @@ public:
void call(Label& label); void call(Label& label);
void call(OperandX64 op); void call(OperandX64 op);
void int3();
// AVX // AVX
void vaddpd(OperandX64 dst, OperandX64 src1, OperandX64 src2); void vaddpd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vaddps(OperandX64 dst, OperandX64 src1, OperandX64 src2); void vaddps(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vaddsd(OperandX64 dst, OperandX64 src1, OperandX64 src2); void vaddsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vaddss(OperandX64 dst, OperandX64 src1, OperandX64 src2); void vaddss(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vsubsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vmulsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vdivsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vxorpd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vcomisd(OperandX64 src1, OperandX64 src2);
void vsqrtpd(OperandX64 dst, OperandX64 src); void vsqrtpd(OperandX64 dst, OperandX64 src);
void vsqrtps(OperandX64 dst, OperandX64 src); void vsqrtps(OperandX64 dst, OperandX64 src);
void vsqrtsd(OperandX64 dst, OperandX64 src1, OperandX64 src2); void vsqrtsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);

View File

@ -37,8 +37,6 @@ enum class Condition
Zero, Zero,
NotZero, NotZero,
// TODO: ordered and unordered floating-point conditions
Count Count
}; };

View File

@ -231,6 +231,7 @@ void AssemblyBuilderX64::lea(OperandX64 lhs, OperandX64 rhs)
if (logText) if (logText)
log("lea", lhs, rhs); log("lea", lhs, rhs);
LUAU_ASSERT(rhs.cat == CategoryX64::mem);
placeBinaryRegAndRegMem(lhs, rhs, 0x8d, 0x8d); placeBinaryRegAndRegMem(lhs, rhs, 0x8d, 0x8d);
} }
@ -314,6 +315,14 @@ void AssemblyBuilderX64::call(OperandX64 op)
commit(); commit();
} }
void AssemblyBuilderX64::int3()
{
if (logText)
log("int3");
place(0xcc);
}
void AssemblyBuilderX64::vaddpd(OperandX64 dst, OperandX64 src1, OperandX64 src2) void AssemblyBuilderX64::vaddpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{ {
placeAvx("vaddpd", dst, src1, src2, 0x58, false, AVX_0F, AVX_66); placeAvx("vaddpd", dst, src1, src2, 0x58, false, AVX_0F, AVX_66);
@ -334,6 +343,31 @@ void AssemblyBuilderX64::vaddss(OperandX64 dst, OperandX64 src1, OperandX64 src2
placeAvx("vaddss", dst, src1, src2, 0x58, false, AVX_0F, AVX_F3); placeAvx("vaddss", dst, src1, src2, 0x58, false, AVX_0F, AVX_F3);
} }
void AssemblyBuilderX64::vsubsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vsubsd", dst, src1, src2, 0x5c, false, AVX_0F, AVX_F2);
}
void AssemblyBuilderX64::vmulsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vmulsd", dst, src1, src2, 0x59, false, AVX_0F, AVX_F2);
}
void AssemblyBuilderX64::vdivsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vdivsd", dst, src1, src2, 0x5e, false, AVX_0F, AVX_F2);
}
void AssemblyBuilderX64::vxorpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vxorpd", dst, src1, src2, 0x57, false, AVX_0F, AVX_66);
}
void AssemblyBuilderX64::vcomisd(OperandX64 src1, OperandX64 src2)
{
placeAvx("vcomisd", src1, src2, 0x2f, false, AVX_0F, AVX_66);
}
void AssemblyBuilderX64::vsqrtpd(OperandX64 dst, OperandX64 src) void AssemblyBuilderX64::vsqrtpd(OperandX64 dst, OperandX64 src)
{ {
placeAvx("vsqrtpd", dst, src, 0x51, false, AVX_0F, AVX_66); placeAvx("vsqrtpd", dst, src, 0x51, false, AVX_0F, AVX_66);
@ -494,9 +528,10 @@ void AssemblyBuilderX64::placeBinaryRegMemAndImm(OperandX64 lhs, OperandX64 rhs,
LUAU_ASSERT(lhs.cat == CategoryX64::reg || lhs.cat == CategoryX64::mem); LUAU_ASSERT(lhs.cat == CategoryX64::reg || lhs.cat == CategoryX64::mem);
LUAU_ASSERT(rhs.cat == CategoryX64::imm); LUAU_ASSERT(rhs.cat == CategoryX64::imm);
SizeX64 size = lhs.base.size; SizeX64 size = lhs.cat == CategoryX64::reg ? lhs.base.size : lhs.memSize;
LUAU_ASSERT(size == SizeX64::byte || size == SizeX64::dword || size == SizeX64::qword);
placeRex(lhs.base); placeRex(lhs);
if (size == SizeX64::byte) if (size == SizeX64::byte)
{ {

View File

@ -0,0 +1,26 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <string.h>
namespace Luau
{
inline bool isFlagExperimental(const char* flag)
{
// Flags in this list are disabled by default in various command-line tools. They may have behavior that is not fully final,
// or critical bugs that are found after the code has been submitted.
static const char* kList[] =
{
"LuauLowerBoundsCalculation",
nullptr, // makes sure we always have at least one entry
};
for (const char* item: kList)
if (item && strcmp(item, flag) == 0)
return true;
return false;
}
}

View File

@ -27,6 +27,7 @@ LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
LUAU_FASTFLAGVARIABLE(LuauCompileFoldBuiltins, false) LUAU_FASTFLAGVARIABLE(LuauCompileFoldBuiltins, false)
LUAU_FASTFLAGVARIABLE(LuauCompileBetterMultret, false) LUAU_FASTFLAGVARIABLE(LuauCompileBetterMultret, false)
LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false)
namespace Luau namespace Luau
{ {
@ -616,7 +617,7 @@ struct Compiler
} }
else else
{ {
AstExprLocal* le = arg->as<AstExprLocal>(); AstExprLocal* le = FFlag::LuauCompileFreeReassign ? getExprLocal(arg) : arg->as<AstExprLocal>();
Variable* lv = le ? variables.find(le->local) : nullptr; Variable* lv = le ? variables.find(le->local) : nullptr;
// if the argument is a local that isn't mutated, we will simply reuse the existing register // if the argument is a local that isn't mutated, we will simply reuse the existing register
@ -2200,19 +2201,27 @@ struct Compiler
compileLValueUse(lv, source, /* set= */ true); compileLValueUse(lv, source, /* set= */ true);
} }
int getExprLocalReg(AstExpr* node) AstExprLocal* getExprLocal(AstExpr* node)
{ {
if (AstExprLocal* expr = node->as<AstExprLocal>()) if (AstExprLocal* expr = node->as<AstExprLocal>())
return expr;
else if (AstExprGroup* expr = node->as<AstExprGroup>())
return getExprLocal(expr->expr);
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
return getExprLocal(expr->expr);
else
return nullptr;
}
int getExprLocalReg(AstExpr* node)
{
if (AstExprLocal* expr = getExprLocal(node))
{ {
// note: this can't check expr->upvalue because upvalues may be upgraded to locals during inlining // note: this can't check expr->upvalue because upvalues may be upgraded to locals during inlining
Local* l = locals.find(expr->local); Local* l = locals.find(expr->local);
return l && l->allocated ? l->reg : -1; return l && l->allocated ? l->reg : -1;
} }
else if (AstExprGroup* expr = node->as<AstExprGroup>())
return getExprLocalReg(expr->expr);
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
return getExprLocalReg(expr->expr);
else else
return -1; return -1;
} }
@ -2498,6 +2507,22 @@ struct Compiler
if (options.optimizationLevel >= 1 && options.debugLevel <= 1 && areLocalsRedundant(stat)) if (options.optimizationLevel >= 1 && options.debugLevel <= 1 && areLocalsRedundant(stat))
return; return;
// Optimization: for 1-1 local assignments, we can reuse the register *if* neither local is mutated
if (FFlag::LuauCompileFreeReassign && options.optimizationLevel >= 1 && stat->vars.size == 1 && stat->values.size == 1)
{
if (AstExprLocal* re = getExprLocal(stat->values.data[0]))
{
Variable* lv = variables.find(stat->vars.data[0]);
Variable* rv = variables.find(re->local);
if (int reg = getExprLocalReg(re); reg >= 0 && (!lv || !lv->written) && (!rv || !rv->written))
{
pushLocal(stat->vars.data[0], uint8_t(reg));
return;
}
}
}
// note: allocReg in this case allocates into parent block register - note that we don't have RegScope here // note: allocReg in this case allocates into parent block register - note that we don't have RegScope here
uint8_t vars = allocReg(stat, unsigned(stat->vars.size)); uint8_t vars = allocReg(stat, unsigned(stat->vars.size));

View File

@ -31,15 +31,15 @@ ISOCLINE_SOURCES=extern/isocline/src/isocline.c
ISOCLINE_OBJECTS=$(ISOCLINE_SOURCES:%=$(BUILD)/%.o) ISOCLINE_OBJECTS=$(ISOCLINE_SOURCES:%=$(BUILD)/%.o)
ISOCLINE_TARGET=$(BUILD)/libisocline.a ISOCLINE_TARGET=$(BUILD)/libisocline.a
TESTS_SOURCES=$(wildcard tests/*.cpp) CLI/FileUtils.cpp CLI/Profiler.cpp CLI/Coverage.cpp CLI/Repl.cpp TESTS_SOURCES=$(wildcard tests/*.cpp) CLI/FileUtils.cpp CLI/Flags.cpp CLI/Profiler.cpp CLI/Coverage.cpp CLI/Repl.cpp
TESTS_OBJECTS=$(TESTS_SOURCES:%=$(BUILD)/%.o) TESTS_OBJECTS=$(TESTS_SOURCES:%=$(BUILD)/%.o)
TESTS_TARGET=$(BUILD)/luau-tests TESTS_TARGET=$(BUILD)/luau-tests
REPL_CLI_SOURCES=CLI/FileUtils.cpp CLI/Profiler.cpp CLI/Coverage.cpp CLI/Repl.cpp CLI/ReplEntry.cpp REPL_CLI_SOURCES=CLI/FileUtils.cpp CLI/Flags.cpp CLI/Profiler.cpp CLI/Coverage.cpp CLI/Repl.cpp CLI/ReplEntry.cpp
REPL_CLI_OBJECTS=$(REPL_CLI_SOURCES:%=$(BUILD)/%.o) REPL_CLI_OBJECTS=$(REPL_CLI_SOURCES:%=$(BUILD)/%.o)
REPL_CLI_TARGET=$(BUILD)/luau REPL_CLI_TARGET=$(BUILD)/luau
ANALYZE_CLI_SOURCES=CLI/FileUtils.cpp CLI/Analyze.cpp ANALYZE_CLI_SOURCES=CLI/FileUtils.cpp CLI/Flags.cpp CLI/Analyze.cpp
ANALYZE_CLI_OBJECTS=$(ANALYZE_CLI_SOURCES:%=$(BUILD)/%.o) ANALYZE_CLI_OBJECTS=$(ANALYZE_CLI_SOURCES:%=$(BUILD)/%.o)
ANALYZE_CLI_TARGET=$(BUILD)/luau-analyze ANALYZE_CLI_TARGET=$(BUILD)/luau-analyze
@ -117,15 +117,18 @@ $(REPL_CLI_TARGET): LDFLAGS+=-lpthread
fuzz-proto fuzz-prototest: LDFLAGS+=build/libprotobuf-mutator/src/libfuzzer/libprotobuf-mutator-libfuzzer.a build/libprotobuf-mutator/src/libprotobuf-mutator.a build/libprotobuf-mutator/external.protobuf/lib/libprotobuf.a fuzz-proto fuzz-prototest: LDFLAGS+=build/libprotobuf-mutator/src/libfuzzer/libprotobuf-mutator-libfuzzer.a build/libprotobuf-mutator/src/libprotobuf-mutator.a build/libprotobuf-mutator/external.protobuf/lib/libprotobuf.a
# pseudo targets # pseudo targets
.PHONY: all test clean coverage format luau-size .PHONY: all test clean coverage format luau-size aliases
all: $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET) $(TESTS_TARGET) all: $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET) $(TESTS_TARGET) aliases
aliases: luau luau-analyze
test: $(TESTS_TARGET) test: $(TESTS_TARGET)
$(TESTS_TARGET) $(TESTS_ARGS) $(TESTS_TARGET) $(TESTS_ARGS)
clean: clean:
rm -rf $(BUILD) rm -rf $(BUILD)
rm -rf luau luau-analyze
coverage: $(TESTS_TARGET) coverage: $(TESTS_TARGET)
$(TESTS_TARGET) --fflags=true $(TESTS_TARGET) --fflags=true

View File

@ -4,6 +4,7 @@ if(NOT ${CMAKE_VERSION} VERSION_LESS "3.19")
target_sources(Luau.Common PRIVATE target_sources(Luau.Common PRIVATE
Common/include/Luau/Common.h Common/include/Luau/Common.h
Common/include/Luau/Bytecode.h Common/include/Luau/Bytecode.h
Common/include/Luau/ExperimentalFlags.h
) )
endif() endif()
@ -220,6 +221,8 @@ if(TARGET Luau.Repl.CLI)
CLI/Coverage.cpp CLI/Coverage.cpp
CLI/FileUtils.h CLI/FileUtils.h
CLI/FileUtils.cpp CLI/FileUtils.cpp
CLI/Flags.h
CLI/Flags.cpp
CLI/Profiler.h CLI/Profiler.h
CLI/Profiler.cpp CLI/Profiler.cpp
CLI/Repl.cpp CLI/Repl.cpp
@ -231,6 +234,8 @@ if(TARGET Luau.Analyze.CLI)
target_sources(Luau.Analyze.CLI PRIVATE target_sources(Luau.Analyze.CLI PRIVATE
CLI/FileUtils.h CLI/FileUtils.h
CLI/FileUtils.cpp CLI/FileUtils.cpp
CLI/Flags.h
CLI/Flags.cpp
CLI/Analyze.cpp) CLI/Analyze.cpp)
endif() endif()
@ -321,6 +326,8 @@ if(TARGET Luau.CLI.Test)
CLI/Coverage.cpp CLI/Coverage.cpp
CLI/FileUtils.h CLI/FileUtils.h
CLI/FileUtils.cpp CLI/FileUtils.cpp
CLI/Flags.h
CLI/Flags.cpp
CLI/Profiler.h CLI/Profiler.h
CLI/Profiler.cpp CLI/Profiler.cpp
CLI/Repl.cpp CLI/Repl.cpp

View File

@ -854,7 +854,7 @@ void lua_rawset(lua_State* L, int idx)
StkId t = index2addr(L, idx); StkId t = index2addr(L, idx);
api_check(L, ttistable(t)); api_check(L, ttistable(t));
if (hvalue(t)->readonly) if (hvalue(t)->readonly)
luaG_runerror(L, "Attempt to modify a readonly table"); luaG_readonlyerror(L);
setobj2t(L, luaH_set(L, hvalue(t), L->top - 2), L->top - 1); setobj2t(L, luaH_set(L, hvalue(t), L->top - 2), L->top - 1);
luaC_barriert(L, hvalue(t), L->top - 1); luaC_barriert(L, hvalue(t), L->top - 1);
L->top -= 2; L->top -= 2;
@ -867,7 +867,7 @@ void lua_rawseti(lua_State* L, int idx, int n)
StkId o = index2addr(L, idx); StkId o = index2addr(L, idx);
api_check(L, ttistable(o)); api_check(L, ttistable(o));
if (hvalue(o)->readonly) if (hvalue(o)->readonly)
luaG_runerror(L, "Attempt to modify a readonly table"); luaG_readonlyerror(L);
setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top - 1); setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top - 1);
luaC_barriert(L, hvalue(o), L->top - 1); luaC_barriert(L, hvalue(o), L->top - 1);
L->top--; L->top--;
@ -890,7 +890,7 @@ int lua_setmetatable(lua_State* L, int objindex)
case LUA_TTABLE: case LUA_TTABLE:
{ {
if (hvalue(obj)->readonly) if (hvalue(obj)->readonly)
luaG_runerror(L, "Attempt to modify a readonly table"); luaG_readonlyerror(L);
hvalue(obj)->metatable = mt; hvalue(obj)->metatable = mt;
if (mt) if (mt)
luaC_objbarrier(L, hvalue(obj), mt); luaC_objbarrier(L, hvalue(obj), mt);

View File

@ -269,6 +269,11 @@ l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2)
luaG_runerror(L, "attempt to index %s with %s", t1, t2); luaG_runerror(L, "attempt to index %s with %s", t1, t2);
} }
l_noret luaG_readonlyerror(lua_State* L)
{
luaG_runerror(L, "attempt to modify a readonly table");
}
static void pusherror(lua_State* L, const char* msg) static void pusherror(lua_State* L, const char* msg)
{ {
CallInfo* ci = L->ci; CallInfo* ci = L->ci;

View File

@ -19,6 +19,7 @@ LUAI_FUNC l_noret luaG_concaterror(lua_State* L, StkId p1, StkId p2);
LUAI_FUNC l_noret luaG_aritherror(lua_State* L, const TValue* p1, const TValue* p2, TMS op); LUAI_FUNC l_noret luaG_aritherror(lua_State* L, const TValue* p1, const TValue* p2, TMS op);
LUAI_FUNC l_noret luaG_ordererror(lua_State* L, const TValue* p1, const TValue* p2, TMS op); LUAI_FUNC l_noret luaG_ordererror(lua_State* L, const TValue* p1, const TValue* p2, TMS op);
LUAI_FUNC l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2); LUAI_FUNC l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2);
LUAI_FUNC l_noret luaG_readonlyerror(lua_State* L);
LUAI_FUNC LUA_PRINTF_ATTR(2, 3) l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...); LUAI_FUNC LUA_PRINTF_ATTR(2, 3) l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...);
LUAI_FUNC void luaG_pusherror(lua_State* L, const char* error); LUAI_FUNC void luaG_pusherror(lua_State* L, const char* error);

View File

@ -79,7 +79,7 @@ static void moveelements(lua_State* L, int srct, int dstt, int f, int e, int t)
Table* dst = hvalue(L->base + (dstt - 1)); Table* dst = hvalue(L->base + (dstt - 1));
if (dst->readonly) if (dst->readonly)
luaG_runerror(L, "Attempt to modify a readonly table"); luaG_readonlyerror(L);
int n = e - f + 1; /* number of elements to move */ int n = e - f + 1; /* number of elements to move */
@ -204,7 +204,7 @@ static int tmove(lua_State* L)
Table* dst = hvalue(L->base + (tt - 1)); Table* dst = hvalue(L->base + (tt - 1));
if (dst->readonly) /* also checked in moveelements, but this blocks resizes of r/o tables */ if (dst->readonly) /* also checked in moveelements, but this blocks resizes of r/o tables */
luaG_runerror(L, "Attempt to modify a readonly table"); luaG_readonlyerror(L);
if (t > 0 && (t - 1) <= dst->sizearray && (t - 1 + n) > dst->sizearray) if (t > 0 && (t - 1) <= dst->sizearray && (t - 1 + n) > dst->sizearray)
{ /* grow the destination table array */ { /* grow the destination table array */
@ -482,7 +482,7 @@ static int tclear(lua_State* L)
Table* tt = hvalue(L->base); Table* tt = hvalue(L->base);
if (tt->readonly) if (tt->readonly)
luaG_runerror(L, "Attempt to modify a readonly table"); luaG_readonlyerror(L);
luaH_clear(tt); luaH_clear(tt);
return 0; return 0;

View File

@ -128,7 +128,7 @@ void luaV_gettable(lua_State* L, const TValue* t, TValue* key, StkId val)
} }
t = tm; /* else repeat with `tm' */ t = tm; /* else repeat with `tm' */
} }
luaG_runerror(L, "loop in gettable"); luaG_runerror(L, "'__index' chain too long; possible loop");
} }
void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val) void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
@ -143,7 +143,7 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
Table* h = hvalue(t); Table* h = hvalue(t);
if (h->readonly) if (h->readonly)
luaG_runerror(L, "Attempt to modify a readonly table"); luaG_readonlyerror(L);
TValue* oldval = luaH_set(L, h, key); /* do a primitive set */ TValue* oldval = luaH_set(L, h, key); /* do a primitive set */
@ -169,7 +169,7 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */ setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */
t = &temp; t = &temp;
} }
luaG_runerror(L, "loop in settable"); luaG_runerror(L, "'__newindex' chain too long; possible loop");
} }
static int call_binTM(lua_State* L, const TValue* p1, const TValue* p2, StkId res, TMS event) static int call_binTM(lua_State* L, const TValue* p1, const TValue* p2, StkId res, TMS event)

View File

@ -139,7 +139,7 @@ function test()
for _, curr_qdr in pairs(negaMax.index_quadruplets) do -- iterate over all index quadruplets for _, curr_qdr in pairs(negaMax.index_quadruplets) do -- iterate over all index quadruplets
-- count the empty positions and positions occupied by the side whos move it is -- count the empty positions and positions occupied by the side whos move it is
local player_plus_fields, player_minus_fields, empties = 0, 0, 0 local player_plus_fields, player_minus_fields, empties = 0, 0, 0
for _, index in pairs(curr_qdr) do -- iterate over all indices for _, index in next, curr_qdr do -- iterate over all indices
if board[index] == 0 then if board[index] == 0 then
empties = empties + 1 empties = empties + 1
elseif board[index] == 1 then elseif board[index] == 1 then
@ -225,4 +225,4 @@ function test()
return t1-t0 return t1-t0
end end
bench.runCode(test, "tictactoe") bench.runCode(test, "tictactoe")

View File

@ -333,7 +333,7 @@ DEFINE_PROTO_FUZZER(const luau::ModuleSet& message)
try try
{ {
Luau::BytecodeBuilder bcb; Luau::BytecodeBuilder bcb;
Luau::compileOrThrow(bcb, parseResult.root, parseNameTable, compileOptions); Luau::compileOrThrow(bcb, parseResult, parseNameTable, compileOptions);
bytecode = bcb.getBytecode(); bytecode = bcb.getBytecode();
} }
catch (const Luau::CompileError&) catch (const Luau::CompileError&)

View File

@ -155,6 +155,13 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseBinaryInstructionForms")
SINGLE_COMPARE(add(qword[rax + r13 * 2 + 0x1b], rsi), 0x4a, 0x01, 0x74, 0x68, 0x1b); SINGLE_COMPARE(add(qword[rax + r13 * 2 + 0x1b], rsi), 0x4a, 0x01, 0x74, 0x68, 0x1b);
SINGLE_COMPARE(add(qword[rbp + rbx * 2], rsi), 0x48, 0x01, 0x74, 0x5d, 0x00); SINGLE_COMPARE(add(qword[rbp + rbx * 2], rsi), 0x48, 0x01, 0x74, 0x5d, 0x00);
SINGLE_COMPARE(add(qword[rsp + r10 * 2 + 0x1b], r10), 0x4e, 0x01, 0x54, 0x54, 0x1b); SINGLE_COMPARE(add(qword[rsp + r10 * 2 + 0x1b], r10), 0x4e, 0x01, 0x54, 0x54, 0x1b);
// [addr], imm
SINGLE_COMPARE(add(byte[rax], 2), 0x80, 0x00, 0x02);
SINGLE_COMPARE(add(dword[rax], 2), 0x83, 0x00, 0x02);
SINGLE_COMPARE(add(dword[rax], 0xabcd), 0x81, 0x00, 0xcd, 0xab, 0x00, 0x00);
SINGLE_COMPARE(add(qword[rax], 2), 0x48, 0x83, 0x00, 0x02);
SINGLE_COMPARE(add(qword[rax], 0xabcd), 0x48, 0x81, 0x00, 0xcd, 0xab, 0x00, 0x00);
} }
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseUnaryInstructionForms") TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "BaseUnaryInstructionForms")
@ -304,6 +311,13 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXBinaryInstructionForms")
SINGLE_COMPARE(vaddps(xmm9, xmm12, xmmword[r9 + r14 * 2 + 0x1c]), 0xc4, 0x01, 0x98, 0x58, 0x4c, 0x71, 0x1c); SINGLE_COMPARE(vaddps(xmm9, xmm12, xmmword[r9 + r14 * 2 + 0x1c]), 0xc4, 0x01, 0x98, 0x58, 0x4c, 0x71, 0x1c);
SINGLE_COMPARE(vaddps(ymm1, ymm2, ymm3), 0xc4, 0xe1, 0xec, 0x58, 0xcb); SINGLE_COMPARE(vaddps(ymm1, ymm2, ymm3), 0xc4, 0xe1, 0xec, 0x58, 0xcb);
SINGLE_COMPARE(vaddps(ymm9, ymm12, ymmword[r9 + r14 * 2 + 0x1c]), 0xc4, 0x01, 0x9c, 0x58, 0x4c, 0x71, 0x1c); SINGLE_COMPARE(vaddps(ymm9, ymm12, ymmword[r9 + r14 * 2 + 0x1c]), 0xc4, 0x01, 0x9c, 0x58, 0x4c, 0x71, 0x1c);
// Coverage for other instructions that follow the same pattern
SINGLE_COMPARE(vsubsd(xmm8, xmm10, xmm14), 0xc4, 0x41, 0xab, 0x5c, 0xc6);
SINGLE_COMPARE(vmulsd(xmm8, xmm10, xmm14), 0xc4, 0x41, 0xab, 0x59, 0xc6);
SINGLE_COMPARE(vdivsd(xmm8, xmm10, xmm14), 0xc4, 0x41, 0xab, 0x5e, 0xc6);
SINGLE_COMPARE(vxorpd(xmm8, xmm10, xmm14), 0xc4, 0x41, 0xa9, 0x57, 0xc6);
} }
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXUnaryMergeInstructionForms") TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXUnaryMergeInstructionForms")
@ -318,6 +332,9 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXUnaryMergeInstructionForms")
SINGLE_COMPARE(vsqrtsd(xmm8, xmm10, qword[r9]), 0xc4, 0x41, 0xab, 0x51, 0x01); SINGLE_COMPARE(vsqrtsd(xmm8, xmm10, qword[r9]), 0xc4, 0x41, 0xab, 0x51, 0x01);
SINGLE_COMPARE(vsqrtss(xmm8, xmm10, xmm14), 0xc4, 0x41, 0xaa, 0x51, 0xc6); SINGLE_COMPARE(vsqrtss(xmm8, xmm10, xmm14), 0xc4, 0x41, 0xaa, 0x51, 0xc6);
SINGLE_COMPARE(vsqrtss(xmm8, xmm10, dword[r9]), 0xc4, 0x41, 0xaa, 0x51, 0x01); SINGLE_COMPARE(vsqrtss(xmm8, xmm10, dword[r9]), 0xc4, 0x41, 0xaa, 0x51, 0x01);
// Coverage for other instructions that follow the same pattern
SINGLE_COMPARE(vcomisd(xmm8, xmm10), 0xc4, 0x41, 0xf9, 0x2f, 0xc2);
} }
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXMoveInstructionForms") TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXMoveInstructionForms")
@ -342,6 +359,11 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXMoveInstructionForms")
SINGLE_COMPARE(vmovups(ymm8, ymmword[r9]), 0xc4, 0x41, 0xfc, 0x10, 0x01); SINGLE_COMPARE(vmovups(ymm8, ymmword[r9]), 0xc4, 0x41, 0xfc, 0x10, 0x01);
} }
TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "MiscInstructions")
{
SINGLE_COMPARE(int3(), 0xcc);
}
TEST_CASE("LogTest") TEST_CASE("LogTest")
{ {
AssemblyBuilderX64 build(/* logText= */ true); AssemblyBuilderX64 build(/* logText= */ true);
@ -366,6 +388,7 @@ TEST_CASE("LogTest")
build.vmovapd(xmmword[rax], xmm11); build.vmovapd(xmmword[rax], xmm11);
build.pop(r12); build.pop(r12);
build.ret(); build.ret();
build.int3();
build.finalize(); build.finalize();
@ -388,6 +411,7 @@ TEST_CASE("LogTest")
vmovapd xmmword ptr [rax],xmm11 vmovapd xmmword ptr [rax],xmm11
pop r12 pop r12
ret ret
int3
)"; )";
CHECK(same); CHECK(same);
} }

View File

@ -3793,6 +3793,8 @@ RETURN R0 1
TEST_CASE("SharedClosure") TEST_CASE("SharedClosure")
{ {
ScopedFastFlag sff("LuauCompileFreeReassign", true);
// closures can be shared even if functions refer to upvalues, as long as upvalues are top-level // closures can be shared even if functions refer to upvalues, as long as upvalues are top-level
CHECK_EQ("\n" + compileFunction(R"( CHECK_EQ("\n" + compileFunction(R"(
local val = ... local val = ...
@ -3940,11 +3942,10 @@ LOADN R2 1
LOADN R0 10 LOADN R0 10
LOADN R1 1 LOADN R1 1
FORNPREP R0 L5 FORNPREP R0 L5
L4: MOVE R3 R2 L4: GETIMPORT R3 1
GETIMPORT R4 1 NEWCLOSURE R4 P2
NEWCLOSURE R5 P2 CAPTURE VAL R2
CAPTURE VAL R3 CALL R3 1 0
CALL R4 1 0
FORNLOOP R0 L4 FORNLOOP R0 L4
L5: RETURN R0 0 L5: RETURN R0 0
)"); )");
@ -6157,4 +6158,88 @@ RETURN R0 1
)"); )");
} }
TEST_CASE("LocalReassign")
{
ScopedFastFlag sff("LuauCompileFreeReassign", true);
// locals can be re-assigned and the register gets reused
CHECK_EQ("\n" + compileFunction0(R"(
local function test(a, b)
local c = a
return c + b
end
)"), R"(
ADD R2 R0 R1
RETURN R2 1
)");
// this works if the expression is using type casts or grouping
CHECK_EQ("\n" + compileFunction0(R"(
local function test(a, b)
local c = (a :: number)
return c + b
end
)"), R"(
ADD R2 R0 R1
RETURN R2 1
)");
// the optimization requires that neither local is mutated
CHECK_EQ("\n" + compileFunction0(R"(
local function test(a, b)
local c = a
c += 0
local d = b
b += 0
return c + d
end
)"), R"(
MOVE R2 R0
ADDK R2 R2 K0
MOVE R3 R1
ADDK R1 R1 K0
ADD R4 R2 R3
RETURN R4 1
)");
// sanity check for two values
CHECK_EQ("\n" + compileFunction0(R"(
local function test(a, b)
local c = a
local d = b
return c + d
end
)"), R"(
ADD R2 R0 R1
RETURN R2 1
)");
// note: we currently only support this for single assignments
CHECK_EQ("\n" + compileFunction0(R"(
local function test(a, b)
local c, d = a, b
return c + d
end
)"), R"(
MOVE R2 R0
MOVE R3 R1
ADD R4 R2 R3
RETURN R4 1
)");
// of course, captures capture the original register as well (by value since it's immutable)
CHECK_EQ("\n" + compileFunction(R"(
local function test(a, b)
local c = a
local d = b
return function() return c + d end
end
)", 1), R"(
NEWCLOSURE R2 P0
CAPTURE VAL R0
CAPTURE VAL R1
RETURN R2 1
)");
}
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -254,9 +254,9 @@ TEST_CASE("Math")
runConformance("math.lua"); runConformance("math.lua");
} }
TEST_CASE("Table") TEST_CASE("Tables")
{ {
runConformance("nextvar.lua", [](lua_State* L) { runConformance("tables.lua", [](lua_State* L) {
lua_pushcfunction( lua_pushcfunction(
L, L,
[](lua_State* L) { [](lua_State* L) {

View File

@ -1025,4 +1025,73 @@ TEST_CASE("check_without_builtin_next")
frontend.check("Module/B"); frontend.check("Module/B");
} }
TEST_CASE_FIXTURE(BuiltinsFixture, "reexport_cyclic_type")
{
ScopedFastFlag sff[] = {
{"LuauForceExportSurfacesToBeNormal", true},
{"LuauLowerBoundsCalculation", true},
{"LuauNormalizeFlagIsConservative", true},
};
fileResolver.source["Module/A"] = R"(
type F<T> = (set: G<T>) -> ()
export type G<T> = {
forEach: (a: F<T>) -> (),
}
function X<T>(a: F<T>): ()
end
return X
)";
fileResolver.source["Module/B"] = R"(
--!strict
local A = require(script.Parent.A)
export type G<T> = A.G<T>
return {
A = A,
}
)";
CheckResult result = frontend.check("Module/B");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "reexport_type_alias")
{
ScopedFastFlag sff[] = {
{"LuauForceExportSurfacesToBeNormal", true},
{"LuauLowerBoundsCalculation", true},
{"LuauNormalizeFlagIsConservative", true},
};
fileResolver.source["Module/A"] = R"(
type KeyOfTestEvents = "test-file-start" | "test-file-success" | "test-file-failure" | "test-case-result"
type unknown = any
export type TestFileEvent<T = KeyOfTestEvents> = (
eventName: T,
args: any --[[ ROBLOX TODO: Unhandled node for type: TSIndexedAccessType ]] --[[ TestEvents[T] ]]
) -> unknown
return {}
)";
fileResolver.source["Module/B"] = R"(
--!strict
local A = require(script.Parent.A)
export type TestFileEvent = A.TestFileEvent
)";
CheckResult result = frontend.check("Module/B");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -56,10 +56,19 @@ TEST_CASE("encode_constants")
AstExprConstantNil nil{Location()}; AstExprConstantNil nil{Location()};
AstExprConstantBool b{Location(), true}; AstExprConstantBool b{Location(), true};
AstExprConstantNumber n{Location(), 8.2}; AstExprConstantNumber n{Location(), 8.2};
AstExprConstantNumber bigNum{Location(), 0.1677721600000003};
AstArray<char> charString;
charString.data = const_cast<char*>("a\x1d\0\\\"b");
charString.size = 6;
AstExprConstantString needsEscaping{Location(), charString};
CHECK_EQ(R"({"type":"AstExprConstantNil","location":"0,0 - 0,0"})", toJson(&nil)); CHECK_EQ(R"({"type":"AstExprConstantNil","location":"0,0 - 0,0"})", toJson(&nil));
CHECK_EQ(R"({"type":"AstExprConstantBool","location":"0,0 - 0,0","value":true})", toJson(&b)); CHECK_EQ(R"({"type":"AstExprConstantBool","location":"0,0 - 0,0","value":true})", toJson(&b));
CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":8.2})", toJson(&n)); CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":8.1999999999999993})", toJson(&n));
CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":0.16777216000000031})", toJson(&bigNum));
CHECK_EQ("{\"type\":\"AstExprConstantString\",\"location\":\"0,0 - 0,0\",\"value\":\"a\\u001d\\u0000\\\\\\\"b\"}", toJson(&needsEscaping));
} }
TEST_CASE("basic_escaping") TEST_CASE("basic_escaping")
@ -87,7 +96,7 @@ TEST_CASE("encode_AstStatBlock")
AstStatBlock block{Location(), bodyArray}; AstStatBlock block{Location(), bodyArray};
CHECK_EQ( CHECK_EQ(
(R"({"type":"AstStatBlock","location":"0,0 - 0,0","body":[{"type":"AstStatLocal","location":"0,0 - 0,0","vars":[{"type":null,"name":"a_local","location":"0,0 - 0,0"}],"values":[]}]})"), (R"({"type":"AstStatBlock","location":"0,0 - 0,0","body":[{"type":"AstStatLocal","location":"0,0 - 0,0","vars":[{"luauType":null,"name":"a_local","type":"AstLocal","location":"0,0 - 0,0"}],"values":[]}]})"),
toJson(&block)); toJson(&block));
} }
@ -106,7 +115,31 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_tables")
CHECK( CHECK(
json == json ==
R"({"type":"AstStatBlock","location":"0,0 - 6,4","body":[{"type":"AstStatLocal","location":"1,8 - 5,9","vars":[{"type":{"type":"AstTypeTable","location":"1,17 - 3,9","props":[{"name":"foo","location":"2,12 - 2,15","type":{"type":"AstTypeReference","location":"2,17 - 2,23","name":"number","parameters":[]}}],"indexer":false},"name":"x","location":"1,14 - 1,15"}],"values":[{"type":"AstExprTable","location":"3,12 - 5,9","items":[{"kind":"record","key":{"type":"AstExprConstantString","location":"4,12 - 4,15","value":"foo"},"value":{"type":"AstExprConstantNumber","location":"4,18 - 4,21","value":123}}]}]}]})"); R"({"type":"AstStatBlock","location":"0,0 - 6,4","body":[{"type":"AstStatLocal","location":"1,8 - 5,9","vars":[{"luauType":{"type":"AstTypeTable","location":"1,17 - 3,9","props":[{"name":"foo","type":"AstTableProp","location":"2,12 - 2,15","propType":{"type":"AstTypeReference","location":"2,17 - 2,23","name":"number","parameters":[]}}],"indexer":null},"name":"x","type":"AstLocal","location":"1,14 - 1,15"}],"values":[{"type":"AstExprTable","location":"3,12 - 5,9","items":[{"type":"AstExprTableItem","kind":"record","key":{"type":"AstExprConstantString","location":"4,12 - 4,15","value":"foo"},"value":{"type":"AstExprConstantNumber","location":"4,18 - 4,21","value":123}}]}]}]})");
}
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_table_array")
{
std::string src = R"(type X = {string})";
AstStatBlock* root = expectParse(src);
std::string json = toJson(root);
CHECK(
json ==
R"({"type":"AstStatBlock","location":"0,0 - 0,17","body":[{"type":"AstStatTypeAlias","location":"0,0 - 0,17","name":"X","generics":[],"genericPacks":[],"type":{"type":"AstTypeTable","location":"0,9 - 0,17","props":[],"indexer":{"location":"0,10 - 0,16","indexType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"number","parameters":[]},"resultType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"string","parameters":[]}}},"exported":false}]})");
}
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_table_indexer")
{
std::string src = R"(type X = {string})";
AstStatBlock* root = expectParse(src);
std::string json = toJson(root);
CHECK(
json ==
R"({"type":"AstStatBlock","location":"0,0 - 0,17","body":[{"type":"AstStatTypeAlias","location":"0,0 - 0,17","name":"X","generics":[],"genericPacks":[],"type":{"type":"AstTypeTable","location":"0,9 - 0,17","props":[],"indexer":{"location":"0,10 - 0,16","indexType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"number","parameters":[]},"resultType":{"type":"AstTypeReference","location":"0,10 - 0,16","name":"string","parameters":[]}}},"exported":false}]})");
} }
TEST_CASE("encode_AstExprGroup") TEST_CASE("encode_AstExprGroup")
@ -132,12 +165,23 @@ TEST_CASE("encode_AstExprGlobal")
CHECK(json == expected); CHECK(json == expected);
} }
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstExprIfThen")
{
AstStat* statement = expectParseStatement("local a = if x then y else z");
std::string_view expected =
R"({"type":"AstStatLocal","location":"0,0 - 0,28","vars":[{"luauType":null,"name":"a","type":"AstLocal","location":"0,6 - 0,7"}],"values":[{"type":"AstExprIfElse","location":"0,10 - 0,28","condition":{"type":"AstExprGlobal","location":"0,13 - 0,14","global":"x"},"hasThen":true,"trueExpr":{"type":"AstExprGlobal","location":"0,20 - 0,21","global":"y"},"hasElse":true,"falseExpr":{"type":"AstExprGlobal","location":"0,27 - 0,28","global":"z"}}]})";
CHECK(toJson(statement) == expected);
}
TEST_CASE("encode_AstExprLocal") TEST_CASE("encode_AstExprLocal")
{ {
AstLocal local{AstName{"foo"}, Location{}, nullptr, 0, 0, nullptr}; AstLocal local{AstName{"foo"}, Location{}, nullptr, 0, 0, nullptr};
AstExprLocal exprLocal{Location{}, &local, false}; AstExprLocal exprLocal{Location{}, &local, false};
CHECK(toJson(&exprLocal) == R"({"type":"AstExprLocal","location":"0,0 - 0,0","local":{"type":null,"name":"foo","location":"0,0 - 0,0"}})"); CHECK(toJson(&exprLocal) == R"({"type":"AstExprLocal","location":"0,0 - 0,0","local":{"luauType":null,"name":"foo","type":"AstLocal","location":"0,0 - 0,0"}})");
} }
TEST_CASE("encode_AstExprVarargs") TEST_CASE("encode_AstExprVarargs")
@ -181,7 +225,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstExprFunction")
AstExpr* expr = expectParseExpr("function (a) return a end"); AstExpr* expr = expectParseExpr("function (a) return a end");
std::string_view expected = std::string_view expected =
R"({"type":"AstExprFunction","location":"0,4 - 0,29","generics":[],"genericPacks":[],"args":[{"type":null,"name":"a","location":"0,14 - 0,15"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,16 - 0,26","body":[{"type":"AstStatReturn","location":"0,17 - 0,25","list":[{"type":"AstExprLocal","location":"0,24 - 0,25","local":{"type":null,"name":"a","location":"0,14 - 0,15"}}]}]},"functionDepth":1,"debugname":"","hasEnd":true})"; R"({"type":"AstExprFunction","location":"0,4 - 0,29","generics":[],"genericPacks":[],"args":[{"luauType":null,"name":"a","type":"AstLocal","location":"0,14 - 0,15"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,16 - 0,26","body":[{"type":"AstStatReturn","location":"0,17 - 0,25","list":[{"type":"AstExprLocal","location":"0,24 - 0,25","local":{"luauType":null,"name":"a","type":"AstLocal","location":"0,14 - 0,15"}}]}]},"functionDepth":1,"debugname":"","hasEnd":true})";
CHECK(toJson(expr) == expected); CHECK(toJson(expr) == expected);
} }
@ -191,7 +235,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstExprTable")
AstExpr* expr = expectParseExpr("{true, key=true, [key2]=true}"); AstExpr* expr = expectParseExpr("{true, key=true, [key2]=true}");
std::string_view expected = std::string_view expected =
R"({"type":"AstExprTable","location":"0,4 - 0,33","items":[{"kind":"item","value":{"type":"AstExprConstantBool","location":"0,5 - 0,9","value":true}},{"kind":"record","key":{"type":"AstExprConstantString","location":"0,11 - 0,14","value":"key"},"value":{"type":"AstExprConstantBool","location":"0,15 - 0,19","value":true}},{"kind":"general","key":{"type":"AstExprGlobal","location":"0,22 - 0,26","global":"key2"},"value":{"type":"AstExprConstantBool","location":"0,28 - 0,32","value":true}}]})"; R"({"type":"AstExprTable","location":"0,4 - 0,33","items":[{"type":"AstExprTableItem","kind":"item","value":{"type":"AstExprConstantBool","location":"0,5 - 0,9","value":true}},{"type":"AstExprTableItem","kind":"record","key":{"type":"AstExprConstantString","location":"0,11 - 0,14","value":"key"},"value":{"type":"AstExprConstantBool","location":"0,15 - 0,19","value":true}},{"type":"AstExprTableItem","kind":"general","key":{"type":"AstExprGlobal","location":"0,22 - 0,26","global":"key2"},"value":{"type":"AstExprConstantBool","location":"0,28 - 0,32","value":true}}]})";
CHECK(toJson(expr) == expected); CHECK(toJson(expr) == expected);
} }
@ -201,7 +245,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstExprUnary")
AstExpr* expr = expectParseExpr("-b"); AstExpr* expr = expectParseExpr("-b");
std::string_view expected = std::string_view expected =
R"({"type":"AstExprUnary","location":"0,4 - 0,6","op":"minus","expr":{"type":"AstExprGlobal","location":"0,5 - 0,6","global":"b"}})"; R"({"type":"AstExprUnary","location":"0,4 - 0,6","op":"Minus","expr":{"type":"AstExprGlobal","location":"0,5 - 0,6","global":"b"}})";
CHECK(toJson(expr) == expected); CHECK(toJson(expr) == expected);
} }
@ -259,7 +303,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatWhile")
AstStat* statement = expectParseStatement("while true do end"); AstStat* statement = expectParseStatement("while true do end");
std::string_view expected = std::string_view expected =
R"({"type":"AtStatWhile","location":"0,0 - 0,17","condition":{"type":"AstExprConstantBool","location":"0,6 - 0,10","value":true},"body":{"type":"AstStatBlock","location":"0,13 - 0,14","body":[]},"hasDo":true,"hasEnd":true})"; R"({"type":"AstStatWhile","location":"0,0 - 0,17","condition":{"type":"AstExprConstantBool","location":"0,6 - 0,10","value":true},"body":{"type":"AstStatBlock","location":"0,13 - 0,14","body":[]},"hasDo":true,"hasEnd":true})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }
@ -279,7 +323,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatBreak")
AstStat* statement = expectParseStatement("while true do break end"); AstStat* statement = expectParseStatement("while true do break end");
std::string_view expected = std::string_view expected =
R"({"type":"AtStatWhile","location":"0,0 - 0,23","condition":{"type":"AstExprConstantBool","location":"0,6 - 0,10","value":true},"body":{"type":"AstStatBlock","location":"0,13 - 0,20","body":[{"type":"AstStatBreak","location":"0,14 - 0,19"}]},"hasDo":true,"hasEnd":true})"; R"({"type":"AstStatWhile","location":"0,0 - 0,23","condition":{"type":"AstExprConstantBool","location":"0,6 - 0,10","value":true},"body":{"type":"AstStatBlock","location":"0,13 - 0,20","body":[{"type":"AstStatBreak","location":"0,14 - 0,19"}]},"hasDo":true,"hasEnd":true})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }
@ -289,7 +333,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatContinue")
AstStat* statement = expectParseStatement("while true do continue end"); AstStat* statement = expectParseStatement("while true do continue end");
std::string_view expected = std::string_view expected =
R"({"type":"AtStatWhile","location":"0,0 - 0,26","condition":{"type":"AstExprConstantBool","location":"0,6 - 0,10","value":true},"body":{"type":"AstStatBlock","location":"0,13 - 0,23","body":[{"type":"AstStatContinue","location":"0,14 - 0,22"}]},"hasDo":true,"hasEnd":true})"; R"({"type":"AstStatWhile","location":"0,0 - 0,26","condition":{"type":"AstExprConstantBool","location":"0,6 - 0,10","value":true},"body":{"type":"AstStatBlock","location":"0,13 - 0,23","body":[{"type":"AstStatContinue","location":"0,14 - 0,22"}]},"hasDo":true,"hasEnd":true})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }
@ -299,7 +343,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatFor")
AstStat* statement = expectParseStatement("for a=0,1 do end"); AstStat* statement = expectParseStatement("for a=0,1 do end");
std::string_view expected = std::string_view expected =
R"({"type":"AstStatFor","location":"0,0 - 0,16","var":{"type":null,"name":"a","location":"0,4 - 0,5"},"from":{"type":"AstExprConstantNumber","location":"0,6 - 0,7","value":0},"to":{"type":"AstExprConstantNumber","location":"0,8 - 0,9","value":1},"body":{"type":"AstStatBlock","location":"0,12 - 0,13","body":[]},"hasDo":true,"hasEnd":true})"; R"({"type":"AstStatFor","location":"0,0 - 0,16","var":{"luauType":null,"name":"a","type":"AstLocal","location":"0,4 - 0,5"},"from":{"type":"AstExprConstantNumber","location":"0,6 - 0,7","value":0},"to":{"type":"AstExprConstantNumber","location":"0,8 - 0,9","value":1},"body":{"type":"AstStatBlock","location":"0,12 - 0,13","body":[]},"hasDo":true,"hasEnd":true})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }
@ -309,7 +353,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatForIn")
AstStat* statement = expectParseStatement("for a in b do end"); AstStat* statement = expectParseStatement("for a in b do end");
std::string_view expected = std::string_view expected =
R"({"type":"AstStatForIn","location":"0,0 - 0,17","vars":[{"type":null,"name":"a","location":"0,4 - 0,5"}],"values":[{"type":"AstExprGlobal","location":"0,9 - 0,10","global":"b"}],"body":{"type":"AstStatBlock","location":"0,13 - 0,14","body":[]},"hasIn":true,"hasDo":true,"hasEnd":true})"; R"({"type":"AstStatForIn","location":"0,0 - 0,17","vars":[{"luauType":null,"name":"a","type":"AstLocal","location":"0,4 - 0,5"}],"values":[{"type":"AstExprGlobal","location":"0,9 - 0,10","global":"b"}],"body":{"type":"AstStatBlock","location":"0,13 - 0,14","body":[]},"hasIn":true,"hasDo":true,"hasEnd":true})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }
@ -329,7 +373,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatLocalFunction")
AstStat* statement = expectParseStatement("local function a(b) return end"); AstStat* statement = expectParseStatement("local function a(b) return end");
std::string_view expected = std::string_view expected =
R"({"type":"AstStatLocalFunction","location":"0,0 - 0,30","name":{"type":null,"name":"a","location":"0,15 - 0,16"},"func":{"type":"AstExprFunction","location":"0,0 - 0,30","generics":[],"genericPacks":[],"args":[{"type":null,"name":"b","location":"0,17 - 0,18"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,19 - 0,27","body":[{"type":"AstStatReturn","location":"0,20 - 0,26","list":[]}]},"functionDepth":1,"debugname":"a","hasEnd":true}})"; R"({"type":"AstStatLocalFunction","location":"0,0 - 0,30","name":{"luauType":null,"name":"a","type":"AstLocal","location":"0,15 - 0,16"},"func":{"type":"AstExprFunction","location":"0,0 - 0,30","generics":[],"genericPacks":[],"args":[{"luauType":null,"name":"b","type":"AstLocal","location":"0,17 - 0,18"}],"vararg":false,"varargLocation":"0,0 - 0,0","body":{"type":"AstStatBlock","location":"0,19 - 0,27","body":[{"type":"AstStatReturn","location":"0,20 - 0,26","list":[]}]},"functionDepth":1,"debugname":"a","hasEnd":true}})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }
@ -349,7 +393,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction")
AstStat* statement = expectParseStatement("declare function foo(x: number): string"); AstStat* statement = expectParseStatement("declare function foo(x: number): string");
std::string_view expected = std::string_view expected =
R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","name":"foo","params":{"types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","parameters":[]}]},"retTypes":{"types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","parameters":[]}]},"generics":[],"genericPacks":[]})"; R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","name":"foo","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","parameters":[]}]},"retTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","parameters":[]}]},"generics":[],"genericPacks":[]})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }
@ -370,11 +414,11 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass")
REQUIRE(2 == root->body.size); REQUIRE(2 == root->body.size);
std::string_view expected1 = std::string_view expected1 =
R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","type":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","parameters":[]}},{"name":"method","type":{"type":"AstTypeFunction","location":"3,21 - 4,11","generics":[],"genericPacks":[],"argTypes":{"types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","parameters":[]}]},"returnTypes":{"types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","parameters":[]}]}}}]})"; R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","parameters":[]}},{"name":"method","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,21 - 4,11","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","parameters":[]}]},"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","parameters":[]}]}}}]})";
CHECK(toJson(root->body.data[0]) == expected1); CHECK(toJson(root->body.data[0]) == expected1);
std::string_view expected2 = std::string_view expected2 =
R"({"type":"AstStatDeclareClass","location":"6,22 - 8,11","name":"Bar","superName":"Foo","props":[{"name":"prop2","type":{"type":"AstTypeReference","location":"7,19 - 7,25","name":"string","parameters":[]}}]})"; R"({"type":"AstStatDeclareClass","location":"6,22 - 8,11","name":"Bar","superName":"Foo","props":[{"name":"prop2","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"7,19 - 7,25","name":"string","parameters":[]}}]})";
CHECK(toJson(root->body.data[1]) == expected2); CHECK(toJson(root->body.data[1]) == expected2);
} }
@ -383,7 +427,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())"); AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
std::string_view expected = std::string_view expected =
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"type":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeFunction","location":"0,10 - 0,35","generics":[],"genericPacks":[],"argTypes":{"types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","parameters":[]}]},"returnTypes":{"types":[{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","parameters":[]}]}]}},{"type":"AstTypeFunction","location":"0,41 - 0,55","generics":[],"genericPacks":[],"argTypes":{"types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","parameters":[]}]},"returnTypes":{"types":[]}}]},"exported":false})"; R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"type":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeFunction","location":"0,10 - 0,35","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","parameters":[]}]},"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","parameters":[]}]}]}},{"type":"AstTypeFunction","location":"0,41 - 0,55","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","parameters":[]}]},"returnTypes":{"type":"AstTypeList","types":[]}}]},"exported":false})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }
@ -411,7 +455,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypePackExplicit")
CHECK(2 == root->body.size); CHECK(2 == root->body.size);
std::string_view expected = std::string_view expected =
R"({"type":"AstStatLocal","location":"2,8 - 2,36","vars":[{"type":{"type":"AstTypeReference","location":"2,17 - 2,36","name":"A","parameters":[{"type":"AstTypePackExplicit","location":"2,19 - 2,20","typeList":{"types":[{"type":"AstTypeReference","location":"2,20 - 2,26","name":"number","parameters":[]},{"type":"AstTypeReference","location":"2,28 - 2,34","name":"string","parameters":[]}]}}]},"name":"a","location":"2,14 - 2,15"}],"values":[]})"; R"({"type":"AstStatLocal","location":"2,8 - 2,36","vars":[{"luauType":{"type":"AstTypeReference","location":"2,17 - 2,36","name":"A","parameters":[{"type":"AstTypePackExplicit","location":"2,19 - 2,20","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"2,20 - 2,26","name":"number","parameters":[]},{"type":"AstTypeReference","location":"2,28 - 2,34","name":"string","parameters":[]}]}}]},"name":"a","type":"AstLocal","location":"2,14 - 2,15"}],"values":[]})";
CHECK(toJson(root->body.data[1]) == expected); CHECK(toJson(root->body.data[1]) == expected);
} }

View File

@ -13,77 +13,6 @@ using namespace Luau;
TEST_SUITE_BEGIN("NonstrictModeTests"); TEST_SUITE_BEGIN("NonstrictModeTests");
TEST_CASE_FIXTURE(Fixture, "globals")
{
CheckResult result = check(R"(
--!nonstrict
foo = true
foo = "now i'm a string!"
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("foo")));
}
TEST_CASE_FIXTURE(Fixture, "globals2")
{
ScopedFastFlag sff[]{
{"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true},
};
CheckResult result = check(R"(
--!nonstrict
foo = function() return 1 end
foo = "now i'm a string!"
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm);
CHECK_EQ("() -> number", toString(tm->wantedType));
CHECK_EQ("string", toString(tm->givenType));
CHECK_EQ("() -> number", toString(requireType("foo")));
}
TEST_CASE_FIXTURE(Fixture, "globals_everywhere")
{
CheckResult result = check(R"(
--!nonstrict
foo = 1
if true then
bar = 2
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("foo")));
CHECK_EQ("any", toString(requireType("bar")));
}
TEST_CASE_FIXTURE(BuiltinsFixture, "function_returns_number_or_string")
{
ScopedFastFlag sff[]{{"LuauReturnTypeInferenceInNonstrict", true}, {"LuauLowerBoundsCalculation", true}};
CheckResult result = check(R"(
--!nonstrict
local function f()
if math.random() > 0.5 then
return 5
else
return "hi"
end
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK("() -> number | string" == toString(requireType("f")));
}
TEST_CASE_FIXTURE(Fixture, "infer_nullary_function") TEST_CASE_FIXTURE(Fixture, "infer_nullary_function")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
@ -106,13 +35,8 @@ TEST_CASE_FIXTURE(Fixture, "infer_nullary_function")
REQUIRE_EQ(0, rets.size()); REQUIRE_EQ(0, rets.size());
} }
TEST_CASE_FIXTURE(Fixture, "first_return_type_dictates_number_of_return_types") TEST_CASE_FIXTURE(Fixture, "infer_the_maximum_number_of_values_the_function_could_return")
{ {
ScopedFastFlag sff[]{
{"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!nonstrict --!nonstrict
function getMinCardCountForWidth(width) function getMinCardCountForWidth(width)
@ -127,18 +51,22 @@ TEST_CASE_FIXTURE(Fixture, "first_return_type_dictates_number_of_return_types")
TypeId t = requireType("getMinCardCountForWidth"); TypeId t = requireType("getMinCardCountForWidth");
REQUIRE(t); REQUIRE(t);
REQUIRE_EQ("(any) -> number", toString(t)); REQUIRE_EQ("(any) -> (...any)", toString(t));
} }
#if 0
// Maybe we want this?
TEST_CASE_FIXTURE(Fixture, "return_annotation_is_still_checked") TEST_CASE_FIXTURE(Fixture, "return_annotation_is_still_checked")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
--!nonstrict
function foo(x): number return 'hello' end function foo(x): number return 'hello' end
)"); )");
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
REQUIRE_NE(*typeChecker.anyType, *requireType("foo"));
} }
#endif
TEST_CASE_FIXTURE(Fixture, "function_parameters_are_any") TEST_CASE_FIXTURE(Fixture, "function_parameters_are_any")
{ {
@ -324,11 +252,6 @@ TEST_CASE_FIXTURE(Fixture, "delay_function_does_not_require_its_argument_to_retu
TEST_CASE_FIXTURE(Fixture, "inconsistent_module_return_types_are_ok") TEST_CASE_FIXTURE(Fixture, "inconsistent_module_return_types_are_ok")
{ {
ScopedFastFlag sff[]{
{"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!nonstrict --!nonstrict
@ -345,7 +268,7 @@ TEST_CASE_FIXTURE(Fixture, "inconsistent_module_return_types_are_ok")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
REQUIRE_EQ("((any) -> string) | {| foo: any |}", toString(getMainModule()->getModuleScope()->returnType)); REQUIRE_EQ("any", toString(getMainModule()->getModuleScope()->returnType));
} }
TEST_CASE_FIXTURE(Fixture, "returning_insufficient_return_values") TEST_CASE_FIXTURE(Fixture, "returning_insufficient_return_values")

View File

@ -621,7 +621,6 @@ TEST_CASE_FIXTURE(Fixture, "normalize_module_return_type")
{ {
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{"LuauLowerBoundsCalculation", true}, {"LuauLowerBoundsCalculation", true},
{"LuauReturnTypeInferenceInNonstrict", true},
}; };
check(R"( check(R"(
@ -642,7 +641,7 @@ TEST_CASE_FIXTURE(Fixture, "normalize_module_return_type")
end end
)"); )");
CHECK_EQ("(any, any) -> (any, any) -> any", toString(getMainModule()->getModuleScope()->returnType)); CHECK_EQ("(any, any) -> (...any)", toString(getMainModule()->getModuleScope()->returnType));
} }
TEST_CASE_FIXTURE(Fixture, "return_type_is_not_a_constrained_intersection") TEST_CASE_FIXTURE(Fixture, "return_type_is_not_a_constrained_intersection")

View File

@ -677,11 +677,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "toposort_doesnt_break_mutual_recursion")
TEST_CASE_FIXTURE(Fixture, "check_function_before_lambda_that_uses_it") TEST_CASE_FIXTURE(Fixture, "check_function_before_lambda_that_uses_it")
{ {
ScopedFastFlag sff[]{
{"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!nonstrict --!nonstrict
@ -690,7 +685,7 @@ TEST_CASE_FIXTURE(Fixture, "check_function_before_lambda_that_uses_it")
end end
return function() return function()
return f() return f():andThen()
end end
)"); )");
@ -817,18 +812,14 @@ TEST_CASE_FIXTURE(Fixture, "calling_function_with_incorrect_argument_type_yields
TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_leak_free_types") TEST_CASE_FIXTURE(BuiltinsFixture, "calling_function_with_anytypepack_doesnt_leak_free_types")
{ {
ScopedFastFlag sff[]{
{"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
--!nonstrict --!nonstrict
function Test(a): ...any function Test(a)
return 1, "" return 1, ""
end end
local tab = {} local tab = {}
table.insert(tab, Test(1)); table.insert(tab, Test(1));
)"); )");
@ -1625,21 +1616,6 @@ TEST_CASE_FIXTURE(Fixture, "occurs_check_failure_in_function_return_type")
CHECK(nullptr != get<OccursCheckFailed>(result.errors[0])); CHECK(nullptr != get<OccursCheckFailed>(result.errors[0]));
} }
TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_type_pack")
{
ScopedFastFlag sff[]{
{"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true},
};
CheckResult result = check(R"(
local function f() return end
local g = function() return f() end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "quantify_constrained_types") TEST_CASE_FIXTURE(Fixture, "quantify_constrained_types")
{ {
ScopedFastFlag sff[]{ ScopedFastFlag sff[]{

View File

@ -516,7 +516,7 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_trailing_nil")
CHECK_EQ(*typeChecker.nilType, *requireType("extra")); CHECK_EQ(*typeChecker.nilType, *requireType("extra"));
} }
TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer") TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer_strict")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
local t = {} local t = {}
@ -531,6 +531,17 @@ TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer")
CHECK_EQ("Cannot iterate over a table without indexer", ge->message); CHECK_EQ("Cannot iterate over a table without indexer", ge->message);
} }
TEST_CASE_FIXTURE(Fixture, "loop_iter_no_indexer_nonstrict")
{
CheckResult result = check(Mode::Nonstrict, R"(
local t = {}
for k, v in t do
end
)");
LUAU_REQUIRE_ERROR_COUNT(0, result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_iter_metamethod") TEST_CASE_FIXTURE(BuiltinsFixture, "loop_iter_iter_metamethod")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(

View File

@ -343,6 +343,20 @@ TEST_CASE_FIXTURE(Fixture, "specialization_binds_with_prototypes_too_early")
LUAU_REQUIRE_ERRORS(result); // Should not have any errors. LUAU_REQUIRE_ERRORS(result); // Should not have any errors.
} }
TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_type_pack")
{
ScopedFastFlag sff[] = {
{"LuauLowerBoundsCalculation", false},
};
CheckResult result = check(R"(
local function f() return end
local g = function() return f() end
)");
LUAU_REQUIRE_ERRORS(result); // Should not have any errors.
}
TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_variadic_pack") TEST_CASE_FIXTURE(Fixture, "weird_fail_to_unify_variadic_pack")
{ {
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {

View File

@ -2990,6 +2990,15 @@ TEST_CASE_FIXTURE(Fixture, "expected_indexer_value_type_extra_2")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
TEST_CASE_FIXTURE(Fixture, "expected_indexer_from_table_union")
{
ScopedFastFlag luauExpectedTableUnionIndexerType{"LuauExpectedTableUnionIndexerType", true};
LUAU_REQUIRE_NO_ERRORS(check(R"(local a: {[string]: {number | string}} = {a = {2, 's'}})"));
LUAU_REQUIRE_NO_ERRORS(check(R"(local a: {[string]: {number | string}}? = {a = {2, 's'}})"));
LUAU_REQUIRE_NO_ERRORS(check(R"(local a: {[string]: {[string]: {string?}}?} = {["a"] = {["b"] = {"a", "b"}}})"));
}
TEST_CASE_FIXTURE(Fixture, "prop_access_on_key_whose_types_mismatches") TEST_CASE_FIXTURE(Fixture, "prop_access_on_key_whose_types_mismatches")
{ {
ScopedFastFlag sff{"LuauReportErrorsOnIndexerKeyMismatch", true}; ScopedFastFlag sff{"LuauReportErrorsOnIndexerKeyMismatch", true};

View File

@ -85,20 +85,19 @@ TEST_CASE_FIXTURE(Fixture, "infer_in_nocheck_mode")
{ {
ScopedFastFlag sff[]{ ScopedFastFlag sff[]{
{"DebugLuauDeferredConstraintResolution", false}, {"DebugLuauDeferredConstraintResolution", false},
{"LuauReturnTypeInferenceInNonstrict", true},
{"LuauLowerBoundsCalculation", true}, {"LuauLowerBoundsCalculation", true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(
--!nocheck --!nocheck
function f(x) function f(x)
return 5 return x
end end
-- we get type information even if there's type errors -- we get type information even if there's type errors
f(1, 2) f(1, 2)
)"); )");
CHECK_EQ("(any) -> number", toString(requireType("f"))); CHECK_EQ("(any) -> (...any)", toString(requireType("f")));
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
@ -355,6 +354,35 @@ TEST_CASE_FIXTURE(Fixture, "check_expr_recursion_limit")
CHECK(nullptr != get<CodeTooComplex>(result.errors[0])); CHECK(nullptr != get<CodeTooComplex>(result.errors[0]));
} }
TEST_CASE_FIXTURE(Fixture, "globals")
{
CheckResult result = check(R"(
--!nonstrict
foo = true
foo = "now i'm a string!"
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ("any", toString(requireType("foo")));
}
TEST_CASE_FIXTURE(Fixture, "globals2")
{
CheckResult result = check(R"(
--!nonstrict
foo = function() return 1 end
foo = "now i'm a string!"
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
REQUIRE(tm);
CHECK_EQ("() -> (...any)", toString(tm->wantedType));
CHECK_EQ("string", toString(tm->givenType));
CHECK_EQ("() -> (...any)", toString(requireType("foo")));
}
TEST_CASE_FIXTURE(Fixture, "globals_are_banned_in_strict_mode") TEST_CASE_FIXTURE(Fixture, "globals_are_banned_in_strict_mode")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(

View File

@ -592,4 +592,24 @@ do
assert(countud() == 3) assert(countud() == 3)
end end
-- test __newindex-as-a-table indirection: this had memory safety bugs in Lua 5.1.0
do
local hit = false
local grandparent = {}
grandparent.__newindex = function(s,k,v)
assert(k == "foo" and v == 10)
hit = true
end
local parent = {}
parent.__newindex = parent
setmetatable(parent, grandparent)
local child = setmetatable({}, parent)
child.foo = 10
assert(hit and child.foo == nil and parent.foo == nil)
end
return"OK" return"OK"

View File

@ -63,7 +63,7 @@ static int testAssertionHandler(const char* expr, const char* file, int line, co
if (debuggerPresent()) if (debuggerPresent())
LUAU_DEBUGBREAK(); LUAU_DEBUGBREAK();
ADD_FAIL_AT(file, line, "Assertion failed: ", expr); ADD_FAIL_AT(file, line, "Assertion failed: ", std::string(expr));
return 1; return 1;
} }

521
tools/faillist.txt Normal file
View File

@ -0,0 +1,521 @@
AnnotationTests.as_expr_does_not_propagate_type_info
AnnotationTests.as_expr_is_bidirectional
AnnotationTests.as_expr_warns_on_unrelated_cast
AnnotationTests.builtin_types_are_not_exported
AnnotationTests.cannot_use_nonexported_type
AnnotationTests.cloned_interface_maintains_pointers_between_definitions
AnnotationTests.corecursive_types_error_on_tight_loop
AnnotationTests.define_generic_type_alias
AnnotationTests.duplicate_type_param_name
AnnotationTests.for_loop_counter_annotation_is_checked
AnnotationTests.function_return_annotations_are_checked
AnnotationTests.generic_aliases_are_cloned_properly
AnnotationTests.instantiate_type_fun_should_not_trip_rbxassert
AnnotationTests.instantiation_clone_has_to_follow
AnnotationTests.interface_types_belong_to_interface_arena
AnnotationTests.luau_ice_triggers_an_ice
AnnotationTests.luau_ice_triggers_an_ice_exception_with_flag
AnnotationTests.luau_ice_triggers_an_ice_exception_with_flag_handler
AnnotationTests.luau_ice_triggers_an_ice_handler
AnnotationTests.luau_print_is_magic_if_the_flag_is_set
AnnotationTests.luau_print_is_not_special_without_the_flag
AnnotationTests.occurs_check_on_cyclic_intersection_typevar
AnnotationTests.occurs_check_on_cyclic_union_typevar
AnnotationTests.self_referential_type_alias
AnnotationTests.too_many_type_params
AnnotationTests.two_type_params
AnnotationTests.type_alias_always_resolve_to_a_real_type
AnnotationTests.type_alias_B_should_check_with_another_aliases_until_a_non_aliased_type
AnnotationTests.type_alias_should_alias_to_number
AnnotationTests.type_aliasing_to_number_should_not_check_given_a_string
AnnotationTests.type_annotations_inside_function_bodies
AnnotationTests.type_assertion_expr
AnnotationTests.typeof_variable_type_annotation_should_return_its_type
AnnotationTests.use_generic_type_alias
AnnotationTests.use_type_required_from_another_file
AstQuery.last_argument_function_call_type
AstQuery::getDocumentationSymbolAtPosition.binding
AstQuery::getDocumentationSymbolAtPosition.event_callback_arg
AstQuery::getDocumentationSymbolAtPosition.overloaded_fn
AstQuery::getDocumentationSymbolAtPosition.prop
AutocompleteTest.argument_types
AutocompleteTest.arguments_to_global_lambda
AutocompleteTest.as_types
AutocompleteTest.autocomplete_boolean_singleton
AutocompleteTest.autocomplete_default_type_pack_parameters
AutocompleteTest.autocomplete_default_type_parameters
AutocompleteTest.autocomplete_documentation_symbols
AutocompleteTest.autocomplete_end_with_fn_exprs
AutocompleteTest.autocomplete_end_with_lambda
AutocompleteTest.autocomplete_explicit_type_pack
AutocompleteTest.autocomplete_first_function_arg_expected_type
AutocompleteTest.autocomplete_for_in_middle_keywords
AutocompleteTest.autocomplete_for_middle_keywords
AutocompleteTest.autocomplete_if_else_regression
AutocompleteTest.autocomplete_if_middle_keywords
AutocompleteTest.autocomplete_ifelse_expressions
AutocompleteTest.autocomplete_on_string_singletons
AutocompleteTest.autocomplete_oop_implicit_self
AutocompleteTest.autocomplete_repeat_middle_keyword
AutocompleteTest.autocomplete_string_singleton_equality
AutocompleteTest.autocomplete_string_singleton_escape
AutocompleteTest.autocomplete_string_singletons
AutocompleteTest.autocomplete_until_expression
AutocompleteTest.autocomplete_until_in_repeat
AutocompleteTest.autocomplete_while_middle_keywords
AutocompleteTest.autocompleteProp_index_function_metamethod_is_variadic
AutocompleteTest.bias_toward_inner_scope
AutocompleteTest.comments
AutocompleteTest.cyclic_table
AutocompleteTest.do_not_overwrite_context_sensitive_kws
AutocompleteTest.do_not_suggest_internal_module_type
AutocompleteTest.do_not_suggest_synthetic_table_name
AutocompleteTest.dont_offer_any_suggestions_from_the_end_of_a_comment
AutocompleteTest.dont_offer_any_suggestions_from_within_a_broken_comment
AutocompleteTest.dont_offer_any_suggestions_from_within_a_broken_comment_at_the_very_end_of_the_file
AutocompleteTest.dont_offer_any_suggestions_from_within_a_comment
AutocompleteTest.dont_suggest_local_before_its_definition
AutocompleteTest.empty_program
AutocompleteTest.function_expr_params
AutocompleteTest.function_in_assignment_has_parentheses
AutocompleteTest.function_in_assignment_has_parentheses_2
AutocompleteTest.function_parameters
AutocompleteTest.function_result_passed_to_function_has_parentheses
AutocompleteTest.function_type_types
AutocompleteTest.generic_types
AutocompleteTest.get_member_completions
AutocompleteTest.get_string_completions
AutocompleteTest.get_suggestions_for_new_statement
AutocompleteTest.get_suggestions_for_the_very_start_of_the_script
AutocompleteTest.global_function_params
AutocompleteTest.global_functions_are_not_scoped_lexically
AutocompleteTest.if_then_else_elseif_completions
AutocompleteTest.if_then_else_full_keywords
AutocompleteTest.keyword_members
AutocompleteTest.keyword_methods
AutocompleteTest.keyword_types
AutocompleteTest.leave_numbers_alone
AutocompleteTest.library_non_self_calls_are_fine
AutocompleteTest.library_self_calls_are_invalid
AutocompleteTest.local_function
AutocompleteTest.local_function_params
AutocompleteTest.local_functions_fall_out_of_scope
AutocompleteTest.local_initializer
AutocompleteTest.local_initializer_2
AutocompleteTest.local_names
AutocompleteTest.local_types_builtin
AutocompleteTest.method_call_inside_function_body
AutocompleteTest.method_call_inside_if_conditional
AutocompleteTest.module_type_members
AutocompleteTest.modules_with_types
AutocompleteTest.nested_member_completions
AutocompleteTest.nested_recursive_function
AutocompleteTest.no_function_name_suggestions
AutocompleteTest.no_incompatible_self_calls
AutocompleteTest.no_incompatible_self_calls_2
AutocompleteTest.no_incompatible_self_calls_on_class
AutocompleteTest.no_incompatible_self_calls_provisional
AutocompleteTest.not_the_var_we_are_defining
AutocompleteTest.optional_members
AutocompleteTest.private_types
AutocompleteTest.recommend_statement_starting_keywords
AutocompleteTest.recursive_function
AutocompleteTest.recursive_function_global
AutocompleteTest.recursive_function_local
AutocompleteTest.return_types
AutocompleteTest.skip_current_local
AutocompleteTest.sometimes_the_metatable_is_an_error
AutocompleteTest.source_module_preservation_and_invalidation
AutocompleteTest.statement_between_two_statements
AutocompleteTest.stop_at_first_stat_when_recommending_keywords
AutocompleteTest.string_prim_non_self_calls_are_avoided
AutocompleteTest.string_prim_self_calls_are_fine
AutocompleteTest.suggest_external_module_type
AutocompleteTest.suggest_table_keys
AutocompleteTest.table_intersection
AutocompleteTest.table_union
AutocompleteTest.type_correct_argument_type_suggestion
AutocompleteTest.type_correct_expected_argument_type_pack_suggestion
AutocompleteTest.type_correct_expected_argument_type_suggestion
AutocompleteTest.type_correct_expected_argument_type_suggestion_optional
AutocompleteTest.type_correct_expected_argument_type_suggestion_self
AutocompleteTest.type_correct_expected_return_type_pack_suggestion
AutocompleteTest.type_correct_expected_return_type_suggestion
AutocompleteTest.type_correct_full_type_suggestion
AutocompleteTest.type_correct_function_no_parenthesis
AutocompleteTest.type_correct_function_return_types
AutocompleteTest.type_correct_function_type_suggestion
AutocompleteTest.type_correct_keywords
AutocompleteTest.type_correct_local_type_suggestion
AutocompleteTest.type_correct_sealed_table
AutocompleteTest.type_correct_suggestion_for_overloads
AutocompleteTest.type_correct_suggestion_in_argument
AutocompleteTest.type_correct_suggestion_in_table
AutocompleteTest.type_scoping_easy
AutocompleteTest.unsealed_table
AutocompleteTest.unsealed_table_2
AutocompleteTest.user_defined_globals
AutocompleteTest.user_defined_local_functions_in_own_definition
BuiltinDefinitionsTest.lib_documentation_symbols
BuiltinTests.aliased_string_format
BuiltinTests.assert_removes_falsy_types
BuiltinTests.assert_removes_falsy_types2
BuiltinTests.assert_removes_falsy_types_even_from_type_pack_tail_but_only_for_the_first_type
BuiltinTests.assert_returns_false_and_string_iff_it_knows_the_first_argument_cannot_be_truthy
BuiltinTests.bad_select_should_not_crash
BuiltinTests.builtin_tables_sealed
BuiltinTests.coroutine_resume_anything_goes
BuiltinTests.coroutine_wrap_anything_goes
BuiltinTests.debug_info_is_crazy
BuiltinTests.debug_traceback_is_crazy
BuiltinTests.dont_add_definitions_to_persistent_types
BuiltinTests.find_capture_types
BuiltinTests.find_capture_types2
BuiltinTests.find_capture_types3
BuiltinTests.gcinfo
BuiltinTests.getfenv
BuiltinTests.global_singleton_types_are_sealed
BuiltinTests.gmatch_capture_types
BuiltinTests.gmatch_capture_types2
BuiltinTests.gmatch_capture_types_balanced_escaped_parens
BuiltinTests.gmatch_capture_types_default_capture
BuiltinTests.gmatch_capture_types_invalid_pattern_fallback_to_builtin
BuiltinTests.gmatch_capture_types_invalid_pattern_fallback_to_builtin2
BuiltinTests.gmatch_capture_types_leading_end_bracket_is_part_of_set
BuiltinTests.gmatch_capture_types_parens_in_sets_are_ignored
BuiltinTests.gmatch_capture_types_set_containing_lbracket
BuiltinTests.gmatch_definition
BuiltinTests.ipairs_iterator_should_infer_types_and_type_check
BuiltinTests.lua_51_exported_globals_all_exist
BuiltinTests.match_capture_types
BuiltinTests.match_capture_types2
BuiltinTests.math_max_checks_for_numbers
BuiltinTests.math_max_variatic
BuiltinTests.math_things_are_defined
BuiltinTests.next_iterator_should_infer_types_and_type_check
BuiltinTests.no_persistent_typelevel_change
BuiltinTests.os_time_takes_optional_date_table
BuiltinTests.pairs_iterator_should_infer_types_and_type_check
BuiltinTests.see_thru_select
BuiltinTests.see_thru_select_count
BuiltinTests.select_on_variadic
BuiltinTests.select_slightly_out_of_range
BuiltinTests.select_way_out_of_range
BuiltinTests.select_with_decimal_argument_is_rounded_down
BuiltinTests.select_with_variadic_typepack_tail
BuiltinTests.select_with_variadic_typepack_tail_and_string_head
BuiltinTests.set_metatable_needs_arguments
BuiltinTests.setmetatable_should_not_mutate_persisted_types
BuiltinTests.setmetatable_unpacks_arg_types_correctly
BuiltinTests.sort
BuiltinTests.sort_with_bad_predicate
BuiltinTests.sort_with_predicate
BuiltinTests.string_format_arg_count_mismatch
BuiltinTests.string_format_arg_types_inference
BuiltinTests.string_format_as_method
BuiltinTests.string_format_correctly_ordered_types
BuiltinTests.string_format_report_all_type_errors_at_correct_positions
BuiltinTests.string_format_use_correct_argument
BuiltinTests.string_format_use_correct_argument2
BuiltinTests.string_lib_self_noself
BuiltinTests.table_concat_returns_string
BuiltinTests.table_dot_remove_optionally_returns_generic
BuiltinTests.table_freeze_is_generic
BuiltinTests.table_insert_correctly_infers_type_of_array_2_args_overload
BuiltinTests.table_insert_correctly_infers_type_of_array_3_args_overload
BuiltinTests.table_pack
BuiltinTests.table_pack_reduce
BuiltinTests.table_pack_variadic
BuiltinTests.thread_is_a_type
BuiltinTests.tonumber_returns_optional_number_type
BuiltinTests.tonumber_returns_optional_number_type2
BuiltinTests.xpcall
DefinitionTests.class_definition_function_prop
DefinitionTests.class_definitions_cannot_extend_non_class
DefinitionTests.class_definitions_cannot_overload_non_function
DefinitionTests.declaring_generic_functions
DefinitionTests.definition_file_class_function_args
DefinitionTests.definition_file_classes
DefinitionTests.definition_file_loading
DefinitionTests.definitions_documentation_symbols
DefinitionTests.documentation_symbols_dont_attach_to_persistent_types
DefinitionTests.load_definition_file_errors_do_not_pollute_global_scope
DefinitionTests.no_cyclic_defined_classes
DefinitionTests.single_class_type_identity_in_global_types
FrontendTest.accumulate_cached_errors
FrontendTest.accumulate_cached_errors_in_consistent_order
FrontendTest.any_annotation_breaks_cycle
FrontendTest.ast_node_at_position
FrontendTest.automatically_check_cyclically_dependent_scripts
FrontendTest.automatically_check_dependent_scripts
FrontendTest.check_without_builtin_next
FrontendTest.clearStats
FrontendTest.cycle_detection_between_check_and_nocheck
FrontendTest.cycle_detection_disabled_in_nocheck
FrontendTest.cycle_error_paths
FrontendTest.cycle_errors_can_be_fixed
FrontendTest.cycle_incremental_type_surface
FrontendTest.cycle_incremental_type_surface_longer
FrontendTest.discard_type_graphs
FrontendTest.dont_recheck_script_that_hasnt_been_marked_dirty
FrontendTest.dont_reparse_clean_file_when_linting
FrontendTest.environments
FrontendTest.find_a_require
FrontendTest.find_a_require_inside_a_function
FrontendTest.ignore_require_to_nonexistent_file
FrontendTest.imported_table_modification_2
FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded
FrontendTest.no_use_after_free_with_type_fun_instantiation
FrontendTest.nocheck_cycle_used_by_checked
FrontendTest.nocheck_modules_are_typed
FrontendTest.produce_errors_for_unchanged_file_with_a_syntax_error
FrontendTest.produce_errors_for_unchanged_file_with_errors
FrontendTest.re_report_type_error_in_required_file
FrontendTest.real_source
FrontendTest.recheck_if_dependent_script_is_dirty
FrontendTest.report_require_to_nonexistent_file
FrontendTest.report_syntax_error_in_required_file
FrontendTest.reports_errors_from_multiple_sources
FrontendTest.stats_are_not_reset_between_checks
FrontendTest.test_lint_uses_correct_config
FrontendTest.test_pruneParentSegments
FrontendTest.trace_requires_in_nonstrict_mode
FrontendTest.typecheck_twice_for_ast_types
isSubtype.functions_and_any
isSubtype.intersection_of_functions_of_different_arities
isSubtype.intersection_of_tables
isSubtype.table_with_any_prop
isSubtype.table_with_table_prop
isSubtype.tables
Linter.BuiltinGlobalWrite
Linter.DeprecatedApi
Linter.LocalShadowGlobal
Linter.TableOperations
Linter.use_all_parent_scopes_for_globals
ModuleTests.any_persistance_does_not_leak
ModuleTests.builtin_types_point_into_globalTypes_arena
ModuleTests.clone_self_property
ModuleTests.deepClone_cyclic_table
NonstrictModeTests.delay_function_does_not_require_its_argument_to_return_anything
NonstrictModeTests.for_in_iterator_variables_are_any
NonstrictModeTests.function_parameters_are_any
NonstrictModeTests.inconsistent_module_return_types_are_ok
NonstrictModeTests.inconsistent_return_types_are_ok
NonstrictModeTests.infer_nullary_function
NonstrictModeTests.infer_the_maximum_number_of_values_the_function_could_return
NonstrictModeTests.inline_table_props_are_also_any
NonstrictModeTests.local_tables_are_not_any
NonstrictModeTests.locals_are_any_by_default
NonstrictModeTests.offer_a_hint_if_you_use_a_dot_instead_of_a_colon
NonstrictModeTests.parameters_having_type_any_are_optional
NonstrictModeTests.returning_insufficient_return_values
NonstrictModeTests.returning_too_many_values
NonstrictModeTests.table_dot_insert_and_recursive_calls
NonstrictModeTests.table_props_are_any
Normalize.any_wins_the_battle_over_unknown_in_unions
Normalize.constrained_intersection_of_intersections
Normalize.cyclic_intersection
Normalize.cyclic_table_is_marked_normal
Normalize.cyclic_table_is_not_marked_normal
Normalize.cyclic_table_normalizes_sensibly
Normalize.cyclic_union
Normalize.fuzz_failure_bound_type_is_normal_but_not_its_bounded_to
Normalize.fuzz_failure_instersection_combine_must_follow
Normalize.higher_order_function
Normalize.intersection_combine_on_bound_self
Normalize.intersection_inside_a_table_inside_another_intersection
Normalize.intersection_inside_a_table_inside_another_intersection_2
Normalize.intersection_inside_a_table_inside_another_intersection_3
Normalize.intersection_inside_a_table_inside_another_intersection_4
Normalize.intersection_of_confluent_overlapping_tables
Normalize.intersection_of_disjoint_tables
Normalize.intersection_of_functions
Normalize.intersection_of_overlapping_tables
Normalize.intersection_of_tables_with_indexers
Normalize.nested_table_normalization_with_non_table__no_ice
Normalize.normalization_does_not_convert_ever
Normalize.normalize_module_return_type
Normalize.normalize_unions_containing_never
Normalize.normalize_unions_containing_unknown
Normalize.return_type_is_not_a_constrained_intersection
Normalize.skip_force_normal_on_external_types
Normalize.union_of_distinct_free_types
Normalize.variadic_tail_is_marked_normal
Normalize.visiting_a_type_twice_is_not_considered_normal
ParseErrorRecovery.empty_function_type_error_recovery
ParseErrorRecovery.extra_table_indexer_recovery
ParseErrorRecovery.extra_token_in_consume
ParseErrorRecovery.extra_token_in_consume_match
ParseErrorRecovery.extra_token_in_consume_match_end
ParseErrorRecovery.generic_type_list_recovery
ParseErrorRecovery.multiple_parse_errors
ParseErrorRecovery.recovery_of_parenthesized_expressions
ParseErrorRecovery.statement_error_recovery_expected
ParseErrorRecovery.statement_error_recovery_unexpected
ParserTests.break_return_not_last_error
ParserTests.continue_not_last_error
ParserTests.error_on_confusable
ParserTests.error_on_non_utf8_sequence
ParserTests.error_on_unicode
ParserTests.export_is_an_identifier_only_when_followed_by_type
ParserTests.functions_cannot_have_return_annotations_if_extensions_are_disabled
ParserTests.illegal_type_alias_if_extensions_are_disabled
ParserTests.incomplete_statement_error
ParserTests.local_cannot_have_annotation_with_extensions_disabled
ParserTests.parse_compound_assignment_error_call
ParserTests.parse_compound_assignment_error_multiple
ParserTests.parse_compound_assignment_error_not_lvalue
ParserTests.parse_error_function_call
ParserTests.parse_error_function_call_newline
ParserTests.parse_error_messages
ParserTests.parse_error_table_literal
ParserTests.parse_error_type_name
ParserTests.parse_nesting_based_end_detection
ParserTests.parse_nesting_based_end_detection_failsafe_earlier
ParserTests.parse_nesting_based_end_detection_local_function
ParserTests.parse_nesting_based_end_detection_local_repeat
ParserTests.parse_nesting_based_end_detection_nested
ParserTests.parse_nesting_based_end_detection_single_line
ParserTests.parse_numbers_error
ParserTests.parse_numbers_range_error
ParserTests.stop_if_line_ends_with_hyphen
ParserTests.type_alias_error_messages
RuntimeLimits.typescript_port_of_Result_type
ToDot.bound_table
ToDot.class
ToDot.function
ToDot.metatable
ToDot.primitive
ToDot.table
ToString.exhaustive_toString_of_cyclic_table
ToString.function_type_with_argument_names
ToString.function_type_with_argument_names_and_self
ToString.function_type_with_argument_names_generic
ToString.named_metatable_toStringNamedFunction
ToString.no_parentheses_around_cyclic_function_type_in_union
ToString.toStringDetailed2
ToString.toStringErrorPack
ToString.toStringNamedFunction_generic_pack
ToString.toStringNamedFunction_hide_type_params
ToString.toStringNamedFunction_id
ToString.toStringNamedFunction_map
ToString.toStringNamedFunction_overrides_param_names
ToString.toStringNamedFunction_variadics
TranspilerTests.attach_types
TranspilerTests.type_lists_should_be_emitted_correctly
TranspilerTests.types_should_not_be_considered_cyclic_if_they_are_not_recursive
TypeAliases.basic_alias
TypeAliases.cannot_steal_hoisted_type_alias
TypeAliases.cli_38393_recursive_intersection_oom
TypeAliases.corecursive_function_types
TypeAliases.corecursive_types_generic
TypeAliases.cyclic_function_type_in_type_alias
TypeAliases.cyclic_types_of_named_table_fields_do_not_expand_when_stringified
TypeAliases.do_not_quantify_unresolved_aliases
TypeAliases.dont_stop_typechecking_after_reporting_duplicate_type_definition
TypeAliases.export_type_and_type_alias_are_duplicates
TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any
TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any_2
TypeAliases.free_variables_from_typeof_in_aliases
TypeAliases.general_require_multi_assign
TypeAliases.generic_param_remap
TypeAliases.generic_typevars_are_not_considered_to_escape_their_scope_if_they_are_reused_in_multiple_aliases
TypeAliases.module_export_free_type_leak
TypeAliases.module_export_wrapped_free_type_leak
TypeAliases.mutually_recursive_aliases
TypeAliases.mutually_recursive_generic_aliases
TypeAliases.mutually_recursive_types_errors
TypeAliases.mutually_recursive_types_restriction_not_ok_1
TypeAliases.mutually_recursive_types_restriction_not_ok_2
TypeAliases.mutually_recursive_types_restriction_ok
TypeAliases.mutually_recursive_types_swapsies_not_ok
TypeAliases.mutually_recursive_types_swapsies_ok
TypeAliases.names_are_ascribed
TypeAliases.non_recursive_aliases_that_reuse_a_generic_name
TypeAliases.recursive_types_restriction_not_ok
TypeAliases.recursive_types_restriction_ok
TypeAliases.reported_location_is_correct_when_type_alias_are_duplicates
TypeAliases.stringify_optional_parameterized_alias
TypeAliases.stringify_type_alias_of_recursive_template_table_type
TypeAliases.stringify_type_alias_of_recursive_template_table_type2
TypeAliases.type_alias_fwd_declaration_is_precise
TypeAliases.type_alias_import_mutation
TypeAliases.type_alias_local_mutation
TypeAliases.type_alias_local_rename
TypeAliases.type_alias_local_synthetic_mutation
TypeAliases.type_alias_of_an_imported_recursive_generic_type
TypeAliases.type_alias_of_an_imported_recursive_type
TypeAliases.use_table_name_and_generic_params_in_errors
TypeInferAnyError.any_type_propagates
TypeInferAnyError.assign_prop_to_table_by_calling_any_yields_any
TypeInferAnyError.call_to_any_yields_any
TypeInferAnyError.calling_error_type_yields_error
TypeInferAnyError.can_get_length_of_any
TypeInferAnyError.can_subscript_any
TypeInferAnyError.CheckMethodsOfAny
TypeInferAnyError.for_in_loop_iterator_is_any
TypeInferAnyError.for_in_loop_iterator_is_any2
TypeInferAnyError.for_in_loop_iterator_is_error
TypeInferAnyError.for_in_loop_iterator_is_error2
TypeInferAnyError.for_in_loop_iterator_returns_any
TypeInferAnyError.for_in_loop_iterator_returns_any2
TypeInferAnyError.indexing_error_type_does_not_produce_an_error
TypeInferAnyError.length_of_error_type_does_not_produce_an_error
TypeInferAnyError.metatable_of_any_can_be_a_table
TypeInferAnyError.prop_access_on_any_with_other_options
TypeInferAnyError.quantify_any_does_not_bind_to_itself
TypeInferAnyError.replace_every_free_type_when_unifying_a_complex_function_with_any
TypeInferAnyError.type_error_addition
TypeInferClasses.assign_to_prop_of_class
TypeInferClasses.call_base_method
TypeInferClasses.call_instance_method
TypeInferClasses.call_method_of_a_child_class
TypeInferClasses.call_method_of_a_class
TypeInferClasses.can_assign_to_prop_of_base_class
TypeInferClasses.can_assign_to_prop_of_base_class_using_string
TypeInferClasses.can_read_prop_of_base_class
TypeInferClasses.can_read_prop_of_base_class_using_string
TypeInferClasses.cannot_call_method_of_child_on_base_instance
TypeInferClasses.cannot_call_unknown_method_of_a_class
TypeInferClasses.cannot_unify_class_instance_with_primitive
TypeInferClasses.class_type_mismatch_with_name_conflict
TypeInferClasses.class_unification_type_mismatch_is_correct_order
TypeInferClasses.classes_can_have_overloaded_operators
TypeInferClasses.classes_without_overloaded_operators_cannot_be_added
TypeInferClasses.detailed_class_unification_error
TypeInferClasses.function_arguments_are_covariant
TypeInferClasses.higher_order_function_arguments_are_contravariant
TypeInferClasses.higher_order_function_return_type_is_not_contravariant
TypeInferClasses.higher_order_function_return_values_are_covariant
TypeInferClasses.optional_class_field_access_error
TypeInferClasses.table_class_unification_reports_sane_errors_for_missing_properties
TypeInferClasses.table_indexers_are_invariant
TypeInferClasses.table_properties_are_invariant
TypeInferClasses.warn_when_prop_almost_matches
TypeInferClasses.we_can_infer_that_a_parameter_must_be_a_particular_class
TypeInferClasses.we_can_report_when_someone_is_trying_to_use_a_table_rather_than_a_class
TypeInferFunctions.another_indirect_function_case_where_it_is_ok_to_provide_too_many_arguments
TypeInferFunctions.another_recursive_local_function
TypeInferFunctions.cannot_hoist_interior_defns_into_signature
TypeInferFunctions.check_function_before_lambda_that_uses_it
TypeInferFunctions.complicated_return_types_require_an_explicit_annotation
TypeInferFunctions.cyclic_function_type_in_args
TypeInferFunctions.dont_give_other_overloads_message_if_only_one_argument_matching_overload_exists
TypeInferFunctions.duplicate_functions_with_different_signatures_not_allowed_in_nonstrict
TypeInferFunctions.first_argument_can_be_optional
TypeInferFunctions.func_expr_doesnt_leak_free
TypeInferFunctions.higher_order_function_2
TypeInferFunctions.higher_order_function_4
TypeInferFunctions.infer_return_type_from_selected_overload
TypeInferFunctions.infer_that_function_does_not_return_a_table
TypeInferFunctions.it_is_ok_not_to_supply_enough_retvals
TypeInferFunctions.it_is_ok_to_oversaturate_a_higher_order_function_argument
TypeInferFunctions.list_all_overloads_if_no_overload_takes_given_argument_count
TypeInferFunctions.list_only_alternative_overloads_that_match_argument_count
TypeInferFunctions.mutual_recursion
TypeInferFunctions.recursive_function
TypeInferFunctions.recursive_local_function
TypeInferFunctions.too_many_arguments
TypeInferFunctions.toposort_doesnt_break_mutual_recursion
TypeInferFunctions.vararg_function_is_quantified
TypeInferFunctions.vararg_functions_should_allow_calls_of_any_types_and_size

View File

@ -38,6 +38,38 @@
<DisplayString Condition="typeId == 29" Optional="true">{{ typeId=29, value={*($T30*)storage} }}</DisplayString> <DisplayString Condition="typeId == 29" Optional="true">{{ typeId=29, value={*($T30*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 30" Optional="true">{{ typeId=30, value={*($T31*)storage} }}</DisplayString> <DisplayString Condition="typeId == 30" Optional="true">{{ typeId=30, value={*($T31*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 31" Optional="true">{{ typeId=31, value={*($T32*)storage} }}</DisplayString> <DisplayString Condition="typeId == 31" Optional="true">{{ typeId=31, value={*($T32*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 32" Optional="true">{{ typeId=32, value={*($T33*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 33" Optional="true">{{ typeId=33, value={*($T34*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 34" Optional="true">{{ typeId=34, value={*($T35*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 35" Optional="true">{{ typeId=35, value={*($T36*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 36" Optional="true">{{ typeId=36, value={*($T37*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 37" Optional="true">{{ typeId=37, value={*($T38*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 38" Optional="true">{{ typeId=38, value={*($T39*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 39" Optional="true">{{ typeId=39, value={*($T40*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 40" Optional="true">{{ typeId=40, value={*($T41*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 41" Optional="true">{{ typeId=41, value={*($T42*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 42" Optional="true">{{ typeId=42, value={*($T43*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 43" Optional="true">{{ typeId=43, value={*($T44*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 44" Optional="true">{{ typeId=44, value={*($T45*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 45" Optional="true">{{ typeId=45, value={*($T46*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 46" Optional="true">{{ typeId=46, value={*($T47*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 47" Optional="true">{{ typeId=47, value={*($T48*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 48" Optional="true">{{ typeId=48, value={*($T49*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 49" Optional="true">{{ typeId=49, value={*($T50*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 50" Optional="true">{{ typeId=50, value={*($T51*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 51" Optional="true">{{ typeId=51, value={*($T52*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 52" Optional="true">{{ typeId=52, value={*($T53*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 53" Optional="true">{{ typeId=53, value={*($T54*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 54" Optional="true">{{ typeId=54, value={*($T55*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 55" Optional="true">{{ typeId=55, value={*($T56*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 56" Optional="true">{{ typeId=56, value={*($T57*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 57" Optional="true">{{ typeId=57, value={*($T58*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 58" Optional="true">{{ typeId=58, value={*($T59*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 59" Optional="true">{{ typeId=59, value={*($T60*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 60" Optional="true">{{ typeId=60, value={*($T61*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 61" Optional="true">{{ typeId=61, value={*($T62*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 62" Optional="true">{{ typeId=62, value={*($T63*)storage} }}</DisplayString>
<DisplayString Condition="typeId == 63" Optional="true">{{ typeId=63, value={*($T64*)storage} }}</DisplayString>
<Expand> <Expand>
<Item Name="typeId">typeId</Item> <Item Name="typeId">typeId</Item>
<Item Name="[value]" Condition="typeId == 0" Optional="true">*($T1*)storage</Item> <Item Name="[value]" Condition="typeId == 0" Optional="true">*($T1*)storage</Item>
@ -72,6 +104,38 @@
<Item Name="[value]" Condition="typeId == 29" Optional="true">*($T30*)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 == 30" Optional="true">*($T31*)storage</Item>
<Item Name="[value]" Condition="typeId == 31" Optional="true">*($T32*)storage</Item> <Item Name="[value]" Condition="typeId == 31" Optional="true">*($T32*)storage</Item>
<Item Name="[value]" Condition="typeId == 32" Optional="true">*($T33*)storage</Item>
<Item Name="[value]" Condition="typeId == 33" Optional="true">*($T34*)storage</Item>
<Item Name="[value]" Condition="typeId == 34" Optional="true">*($T35*)storage</Item>
<Item Name="[value]" Condition="typeId == 35" Optional="true">*($T36*)storage</Item>
<Item Name="[value]" Condition="typeId == 36" Optional="true">*($T37*)storage</Item>
<Item Name="[value]" Condition="typeId == 37" Optional="true">*($T38*)storage</Item>
<Item Name="[value]" Condition="typeId == 38" Optional="true">*($T39*)storage</Item>
<Item Name="[value]" Condition="typeId == 39" Optional="true">*($T40*)storage</Item>
<Item Name="[value]" Condition="typeId == 40" Optional="true">*($T41*)storage</Item>
<Item Name="[value]" Condition="typeId == 41" Optional="true">*($T42*)storage</Item>
<Item Name="[value]" Condition="typeId == 42" Optional="true">*($T43*)storage</Item>
<Item Name="[value]" Condition="typeId == 43" Optional="true">*($T44*)storage</Item>
<Item Name="[value]" Condition="typeId == 44" Optional="true">*($T45*)storage</Item>
<Item Name="[value]" Condition="typeId == 45" Optional="true">*($T46*)storage</Item>
<Item Name="[value]" Condition="typeId == 46" Optional="true">*($T47*)storage</Item>
<Item Name="[value]" Condition="typeId == 47" Optional="true">*($T48*)storage</Item>
<Item Name="[value]" Condition="typeId == 48" Optional="true">*($T49*)storage</Item>
<Item Name="[value]" Condition="typeId == 49" Optional="true">*($T50*)storage</Item>
<Item Name="[value]" Condition="typeId == 50" Optional="true">*($T51*)storage</Item>
<Item Name="[value]" Condition="typeId == 51" Optional="true">*($T52*)storage</Item>
<Item Name="[value]" Condition="typeId == 52" Optional="true">*($T53*)storage</Item>
<Item Name="[value]" Condition="typeId == 53" Optional="true">*($T54*)storage</Item>
<Item Name="[value]" Condition="typeId == 54" Optional="true">*($T55*)storage</Item>
<Item Name="[value]" Condition="typeId == 55" Optional="true">*($T56*)storage</Item>
<Item Name="[value]" Condition="typeId == 56" Optional="true">*($T57*)storage</Item>
<Item Name="[value]" Condition="typeId == 57" Optional="true">*($T58*)storage</Item>
<Item Name="[value]" Condition="typeId == 58" Optional="true">*($T59*)storage</Item>
<Item Name="[value]" Condition="typeId == 59" Optional="true">*($T60*)storage</Item>
<Item Name="[value]" Condition="typeId == 60" Optional="true">*($T61*)storage</Item>
<Item Name="[value]" Condition="typeId == 61" Optional="true">*($T62*)storage</Item>
<Item Name="[value]" Condition="typeId == 62" Optional="true">*($T63*)storage</Item>
<Item Name="[value]" Condition="typeId == 63" Optional="true">*($T64*)storage</Item>
</Expand> </Expand>
</Type> </Type>

124
tools/test_dcr.py Normal file
View File

@ -0,0 +1,124 @@
# This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
import argparse
import os.path
import subprocess as sp
import sys
import xml.sax as x
SCRIPT_PATH = os.path.split(sys.argv[0])[0]
FAIL_LIST_PATH = os.path.join(SCRIPT_PATH, "faillist.txt")
def loadFailList():
with open(FAIL_LIST_PATH) as f:
return set(map(str.strip, f.readlines()))
class Handler(x.ContentHandler):
def __init__(self, failList):
self.currentTest = []
self.failList = failList # Set of dotted test names that are expected to fail
self.results = {} # {DottedName: TrueIfTheTestPassed}
def startElement(self, name, attrs):
if name == "TestSuite":
self.currentTest.append(attrs["name"])
elif name == "TestCase":
self.currentTest.append(attrs["name"])
elif name == "OverallResultsAsserts":
if self.currentTest:
try:
failed = 0 != int(attrs["failures"])
except ValueError:
failed = False
dottedName = ".".join(self.currentTest)
shouldFail = dottedName in self.failList
if failed and not shouldFail:
print("UNEXPECTED: {} should have passed".format(dottedName))
elif not failed and shouldFail:
print("UNEXPECTED: {} should have failed".format(dottedName))
self.results[dottedName] = not failed
def endElement(self, name):
if name == "TestCase":
self.currentTest.pop()
elif name == "TestSuite":
self.currentTest.pop()
def main():
parser = argparse.ArgumentParser(
description="Run Luau.UnitTest with deferred constraint resolution enabled"
)
parser.add_argument(
"path", action="store", help="Path to the Luau.UnitTest executable"
)
parser.add_argument(
"--dump",
dest="dump",
action="store_true",
help="Instead of doing any processing, dump the raw output of the test run. Useful for debugging this tool.",
)
parser.add_argument(
"--write",
dest="write",
action="store_true",
help="Write a new faillist.txt after running tests.",
)
args = parser.parse_args()
failList = loadFailList()
p = sp.Popen(
[
args.path,
"--reporters=xml",
"--fflags=true,DebugLuauDeferredConstraintResolution=true",
],
stdout=sp.PIPE,
)
handler = Handler(failList)
if args.dump:
for line in p.stdout:
sys.stdout.buffer.write(line)
return
else:
x.parse(p.stdout, handler)
p.wait()
if args.write:
newFailList = sorted(
(
dottedName
for dottedName, passed in handler.results.items()
if not passed
),
key=str.lower,
)
with open(FAIL_LIST_PATH, "w", newline="\n") as f:
for name in newFailList:
print(name, file=f)
print("Updated faillist.txt")
sys.exit(
0
if all(
not passed == (dottedName in failList)
for dottedName, passed in handler.results.items()
)
else 1
)
if __name__ == "__main__":
main()