mirror of
https://github.com/luau-lang/luau.git
synced 2025-08-26 11:27:08 +01:00
Sync to upstream/release/673 (#1812)
# General Changes * Remove a `static_assert` that prevented Luau from building on 32-bit targets. * Fix `proxyrequire` (see https://github.com/luau-lang/luau/pull/1804) * Replace contents API with new `loadname` API in Luau.Require * Store the positions of `:` symbols in the CST. * Fix a minor bug in the new incremental autocomplete engine: Properly suggest `else` and `elseif` if the cursor is currently within an `if` block. # New Type Solver * Allow generics to be substituted for negation types when performing subtype tests * Crash fixes * Surface subtyping errors more consistently. * Avoid creating double-negation types (like `~~(false?)`) when generating constraints. # Internal Contributors Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Sora Kanosue <skanosue@roblox.com> Co-authored-by: Talha Pathan <tpathan@roblox.com> Co-authored-by: Varun Saini <vsaini@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com> Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com> Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com> Co-authored-by: Menarul Alam <malam@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Ariel Weiss <aaronweiss@roblox.com>
This commit is contained in:
parent
42281aa8de
commit
3eb0c13678
60 changed files with 1325 additions and 2383 deletions
|
@ -51,6 +51,10 @@ struct GeneralizationConstraint
|
|||
|
||||
std::vector<TypeId> interiorTypes;
|
||||
bool hasDeprecatedAttribute = false;
|
||||
|
||||
/// If true, never introduce generics. Always replace free types by their
|
||||
/// bounds or unknown. Presently used only to generalize the whole module.
|
||||
bool noGenerics = false;
|
||||
};
|
||||
|
||||
// variables ~ iterate iterator
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include "Luau/TypeOrPack.h"
|
||||
#include "Luau/TypeUtils.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -40,29 +38,18 @@ struct Reasonings
|
|||
|
||||
std::string toString()
|
||||
{
|
||||
if (FFlag::LuauImproveTypePathsInErrors && reasons.empty())
|
||||
if (reasons.empty())
|
||||
return "";
|
||||
|
||||
// DenseHashSet ordering is entirely undefined, so we want to
|
||||
// sort the reasons here to achieve a stable error
|
||||
// stringification.
|
||||
std::sort(reasons.begin(), reasons.end());
|
||||
std::string allReasons = FFlag::LuauImproveTypePathsInErrors ? "\nthis is because " : "";
|
||||
bool first = true;
|
||||
std::string allReasons = "\nthis is because ";
|
||||
for (const std::string& reason : reasons)
|
||||
{
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
if (reasons.size() > 1)
|
||||
allReasons += "\n\t * ";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
allReasons += "\n\t";
|
||||
}
|
||||
if (reasons.size() > 1)
|
||||
allReasons += "\n\t * ";
|
||||
|
||||
allReasons += reason;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFormatUseLastPosition)
|
||||
|
||||
namespace Luau
|
||||
|
@ -1616,11 +1616,11 @@ bool MagicFreeze::infer(const MagicFunctionCallContext& context)
|
|||
std::optional<DefId> resultDef = dfg->getDefOptional(targetExpr);
|
||||
std::optional<TypeId> resultTy = resultDef ? scope->lookup(*resultDef) : std::nullopt;
|
||||
|
||||
if (FFlag::LuauMagicFreezeCheckBlocked)
|
||||
if (FFlag::LuauMagicFreezeCheckBlocked2)
|
||||
{
|
||||
if (resultTy && !get<BlockedType>(resultTy))
|
||||
if (resultTy && !get<BlockedType>(follow(resultTy)))
|
||||
{
|
||||
// If there's an existing result type but it's _not_ blocked, then
|
||||
// If there's an existing result type, but it's _not_ blocked, then
|
||||
// we aren't type stating this builtin and should fall back to
|
||||
// regular inference.
|
||||
return false;
|
||||
|
@ -1629,6 +1629,8 @@ bool MagicFreeze::infer(const MagicFunctionCallContext& context)
|
|||
|
||||
std::optional<TypeId> frozenType = freezeTable(inputType, context);
|
||||
|
||||
// At this point: we know for sure that if `resultTy` exists, it is a
|
||||
// blocked type, and can safely emplace it.
|
||||
if (!frozenType)
|
||||
{
|
||||
if (resultTy)
|
||||
|
|
|
@ -14,7 +14,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTINTVARIABLE(LuauTypeCloneIterationLimit, 100'000)
|
||||
LUAU_FASTFLAGVARIABLE(LuauClonedTableAndFunctionTypesMustHaveScopes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDoNotClonePersistentBindings)
|
||||
LUAU_FASTFLAG(LuauIncrementalAutocompleteDemandBasedCloning)
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -549,11 +548,6 @@ public:
|
|||
void cloneChildren(LazyType* t) override
|
||||
{
|
||||
// Do not clone lazy types
|
||||
if (!FFlag::LuauIncrementalAutocompleteDemandBasedCloning)
|
||||
{
|
||||
if (auto unwrapped = t->unwrapped.load())
|
||||
t->unwrapped.store(shallowClone(unwrapped));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ LUAU_FASTFLAGVARIABLE(LuauUngeneralizedTypesForRecursiveFunctions)
|
|||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRetainDefinitionAliasLocations)
|
||||
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCacheInferencePerAstExpr)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysResolveAstTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauWeakNilRefinementType)
|
||||
|
@ -52,6 +51,7 @@ LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
|||
LUAU_FASTFLAGVARIABLE(LuauNoTypeFunctionsNamedTypeOf)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAvoidDoubleNegation)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -288,7 +288,9 @@ void ConstraintGenerator::visitModuleRoot(AstStatBlock* block)
|
|||
GeneralizationConstraint{
|
||||
result,
|
||||
moduleFnTy,
|
||||
std::vector<TypeId>{},
|
||||
/*interiorTypes*/ std::vector<TypeId>{},
|
||||
/*hasDeprecatedAttribute*/ false,
|
||||
/*noGenerics*/ true
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -573,7 +575,17 @@ void ConstraintGenerator::computeRefinement(
|
|||
|
||||
// if we have a negative sense, then we need to negate the discriminant
|
||||
if (!sense)
|
||||
discriminantTy = arena->addType(NegationType{discriminantTy});
|
||||
{
|
||||
if (FFlag::LuauAvoidDoubleNegation)
|
||||
{
|
||||
if (auto nt = get<NegationType>(follow(discriminantTy)))
|
||||
discriminantTy = nt->ty;
|
||||
else
|
||||
discriminantTy = arena->addType(NegationType{discriminantTy});
|
||||
}
|
||||
else
|
||||
discriminantTy = arena->addType(NegationType{discriminantTy});
|
||||
}
|
||||
|
||||
if (eq)
|
||||
discriminantTy = createTypeFunctionInstance(builtinTypeFunctions().singletonFunc, {discriminantTy}, {}, scope, location);
|
||||
|
@ -1377,7 +1389,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatRepeat* rep
|
|||
|
||||
static void propagateDeprecatedAttributeToConstraint(ConstraintV& c, const AstExprFunction* func)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
if (GeneralizationConstraint* genConstraint = c.get_if<GeneralizationConstraint>())
|
||||
{
|
||||
genConstraint->hasDeprecatedAttribute = func->hasAttribute(AstAttr::Type::Deprecated);
|
||||
|
@ -1386,7 +1397,6 @@ static void propagateDeprecatedAttributeToConstraint(ConstraintV& c, const AstEx
|
|||
|
||||
static void propagateDeprecatedAttributeToType(TypeId signature, const AstExprFunction* func)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
FunctionType* fty = getMutable<FunctionType>(signature);
|
||||
LUAU_ASSERT(fty);
|
||||
fty->isDeprecatedFunction = func->hasAttribute(AstAttr::Type::Deprecated);
|
||||
|
@ -1429,8 +1439,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
|
|||
std::unique_ptr<Constraint> c =
|
||||
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
|
||||
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
propagateDeprecatedAttributeToConstraint(c->c, function->func);
|
||||
propagateDeprecatedAttributeToConstraint(c->c, function->func);
|
||||
|
||||
Constraint* previous = nullptr;
|
||||
forEachConstraint(
|
||||
|
@ -1457,8 +1466,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatLocalFuncti
|
|||
else
|
||||
{
|
||||
module->astTypes[function->func] = sig.signature;
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
propagateDeprecatedAttributeToType(sig.signature, function->func);
|
||||
propagateDeprecatedAttributeToType(sig.signature, function->func);
|
||||
}
|
||||
|
||||
return ControlFlow::None;
|
||||
|
@ -1502,8 +1510,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
|||
if (sigFullyDefined)
|
||||
{
|
||||
emplaceType<BoundType>(asMutable(generalizedType), sig.signature);
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
propagateDeprecatedAttributeToType(sig.signature, function->func);
|
||||
propagateDeprecatedAttributeToType(sig.signature, function->func);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1512,8 +1519,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatFunction* f
|
|||
NotNull<Constraint> c = addConstraint(constraintScope, function->name->location, GeneralizationConstraint{generalizedType, sig.signature});
|
||||
getMutable<BlockedType>(generalizedType)->setOwner(c);
|
||||
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
propagateDeprecatedAttributeToConstraint(c->c, function->func);
|
||||
propagateDeprecatedAttributeToConstraint(c->c, function->func);
|
||||
|
||||
Constraint* previous = nullptr;
|
||||
forEachConstraint(
|
||||
|
@ -2054,8 +2060,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc
|
|||
|
||||
FunctionType* ftv = getMutable<FunctionType>(fnType);
|
||||
ftv->isCheckedFunction = global->isCheckedFunction();
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
ftv->isDeprecatedFunction = global->hasAttribute(AstAttr::Type::Deprecated);
|
||||
ftv->isDeprecatedFunction = global->hasAttribute(AstAttr::Type::Deprecated);
|
||||
|
||||
ftv->argNames.reserve(global->paramNames.size);
|
||||
for (const auto& el : global->paramNames)
|
||||
|
@ -3710,8 +3715,7 @@ TypeId ConstraintGenerator::resolveFunctionType(
|
|||
// how to quantify/instantiate it.
|
||||
FunctionType ftv{TypeLevel{}, {}, {}, argTypes, returnTypes};
|
||||
ftv.isCheckedFunction = fn->isCheckedFunction();
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
ftv.isDeprecatedFunction = fn->hasAttribute(AstAttr::Type::Deprecated);
|
||||
ftv.isDeprecatedFunction = fn->hasAttribute(AstAttr::Type::Deprecated);
|
||||
|
||||
// This replicates the behavior of the appropriate FunctionType
|
||||
// constructors.
|
||||
|
|
|
@ -36,10 +36,10 @@ LUAU_FASTFLAGVARIABLE(LuauHasPropProperBlock)
|
|||
LUAU_FASTFLAGVARIABLE(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTrackInferredFunctionTypeFromCall)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGuardAgainstMalformedTypeAliasExpansion)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -870,13 +870,10 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
|||
else
|
||||
unify(constraint, generalizedType, *generalizedTy);
|
||||
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
if (FunctionType* fty = getMutable<FunctionType>(follow(generalizedType)))
|
||||
{
|
||||
if (FunctionType* fty = getMutable<FunctionType>(follow(generalizedType)))
|
||||
{
|
||||
if (c.hasDeprecatedAttribute)
|
||||
fty->isDeprecatedFunction = true;
|
||||
}
|
||||
if (c.hasDeprecatedAttribute)
|
||||
fty->isDeprecatedFunction = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -933,6 +930,23 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
|
|||
}
|
||||
}
|
||||
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
if (c.noGenerics)
|
||||
{
|
||||
if (auto ft = getMutable<FunctionType>(c.sourceType))
|
||||
{
|
||||
for (TypeId gen : ft->generics)
|
||||
asMutable(gen)->ty.emplace<BoundType>(builtinTypes->unknownType);
|
||||
ft->generics.clear();
|
||||
|
||||
for (TypePackId gen : ft->genericPacks)
|
||||
asMutable(gen)->ty.emplace<BoundTypePack>(builtinTypes->unknownTypePack);
|
||||
ft->genericPacks.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1213,7 +1227,19 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
|||
// deterministic.
|
||||
if (TypeId* cached = instantiatedAliases.find(signature))
|
||||
{
|
||||
bindResult(*cached);
|
||||
// However, we might now be revealing a malformed mutually recursive
|
||||
// alias. `instantiatedAliases` can change from underneath us in a
|
||||
// way that can cause a cached type id to bind to itself if we don't
|
||||
// do this check.
|
||||
if (FFlag::LuauGuardAgainstMalformedTypeAliasExpansion && occursCheck(follow(c.target), *cached))
|
||||
{
|
||||
reportError(OccursCheckFailed{}, constraint->location);
|
||||
bindResult(errorRecoveryType());
|
||||
}
|
||||
else
|
||||
{
|
||||
bindResult(*cached);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1633,45 +1659,13 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
|||
for (auto generic : ftv->generics)
|
||||
{
|
||||
replacements[generic] = builtinTypes->unknownType;
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
containsGenerics.generics.insert(generic);
|
||||
containsGenerics.generics.insert(generic);
|
||||
}
|
||||
|
||||
for (auto genericPack : ftv->genericPacks)
|
||||
{
|
||||
replacementPacks[genericPack] = builtinTypes->unknownTypePack;
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
containsGenerics.generics.insert(genericPack);
|
||||
}
|
||||
|
||||
// If the type of the function has generics, we don't actually want to push any of the generics themselves
|
||||
// into the argument types as expected types because this creates an unnecessary loop. Instead, we want to
|
||||
// replace these types with `unknown` (and `...unknown`) to keep any structure but not create the cycle.
|
||||
if (!FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
{
|
||||
if (!replacements.empty() || !replacementPacks.empty())
|
||||
{
|
||||
Replacer replacer{arena, std::move(replacements), std::move(replacementPacks)};
|
||||
|
||||
std::optional<TypeId> res = replacer.substitute(fn);
|
||||
if (res)
|
||||
{
|
||||
if (*res != fn)
|
||||
{
|
||||
FunctionType* ftvMut = getMutable<FunctionType>(*res);
|
||||
LUAU_ASSERT(ftvMut);
|
||||
ftvMut->generics.clear();
|
||||
ftvMut->genericPacks.clear();
|
||||
}
|
||||
|
||||
fn = *res;
|
||||
ftv = get<FunctionType>(*res);
|
||||
LUAU_ASSERT(ftv);
|
||||
|
||||
// we've potentially copied type functions here, so we need to reproduce their reduce constraint.
|
||||
reproduceConstraints(constraint->scope, constraint->location, replacer);
|
||||
}
|
||||
}
|
||||
containsGenerics.generics.insert(genericPack);
|
||||
}
|
||||
|
||||
const std::vector<TypeId> expectedArgs = flatten(ftv->argTypes).first;
|
||||
|
@ -1690,7 +1684,7 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull<con
|
|||
(*c.astExpectedTypes)[expr] = expectedArgTy;
|
||||
|
||||
// Generic types are skipped over entirely, for now.
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes && containsGenerics.hasGeneric(expectedArgTy))
|
||||
if (containsGenerics.hasGeneric(expectedArgTy))
|
||||
continue;
|
||||
|
||||
const FunctionType* expectedLambdaTy = get<FunctionType>(expectedArgTy);
|
||||
|
@ -1857,7 +1851,7 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
|||
return true;
|
||||
}
|
||||
|
||||
if (auto ft = get<FreeType>(subjectType))
|
||||
if (auto ft = getMutable<FreeType>(subjectType))
|
||||
{
|
||||
if (auto tbl = get<TableType>(follow(ft->upperBound)); tbl && tbl->indexer)
|
||||
{
|
||||
|
@ -1874,7 +1868,19 @@ bool ConstraintSolver::tryDispatchHasIndexer(
|
|||
TypeId upperBound =
|
||||
arena->addType(TableType{/* props */ {}, TableIndexer{indexType, resultType}, TypeLevel{}, ft->scope, TableState::Unsealed});
|
||||
|
||||
unify(constraint, subjectType, upperBound);
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
{
|
||||
TypeId sr = follow(simplifyIntersection(constraint->scope, constraint->location, ft->upperBound, upperBound));
|
||||
|
||||
if (get<NeverType>(sr))
|
||||
bind(constraint, resultType, builtinTypes->errorType);
|
||||
else
|
||||
ft->upperBound = sr;
|
||||
}
|
||||
else
|
||||
{
|
||||
unify(constraint, subjectType, upperBound);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -133,10 +133,7 @@ struct ErrorConverter
|
|||
size_t luauIndentTypeMismatchMaxTypeLength = size_t(FInt::LuauIndentTypeMismatchMaxTypeLength);
|
||||
if (givenType.length() <= luauIndentTypeMismatchMaxTypeLength || wantedType.length() <= luauIndentTypeMismatchMaxTypeLength)
|
||||
return "Type " + given + " could not be converted into " + wanted;
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
return "Type\n\t" + given + "\ncould not be converted into\n\t" + wanted;
|
||||
else
|
||||
return "Type\n " + given + "\ncould not be converted into\n " + wanted;
|
||||
return "Type\n\t" + given + "\ncould not be converted into\n\t" + wanted;
|
||||
};
|
||||
|
||||
if (givenTypeName == wantedTypeName)
|
||||
|
|
|
@ -33,7 +33,6 @@ LUAU_FASTFLAGVARIABLE(LuauBetterCursorInCommentDetection)
|
|||
LUAU_FASTFLAGVARIABLE(LuauAllFreeTypesHaveScopes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPersistConstraintGenerationScopes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauCloneTypeAliasBindings)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIncrementalAutocompleteDemandBasedCloning)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFragmentNoTypeFunEval)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection)
|
||||
|
@ -41,51 +40,11 @@ LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection)
|
|||
LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
void copyModuleVec(std::vector<T>& result, const std::vector<T>& input)
|
||||
{
|
||||
result.insert(result.end(), input.begin(), input.end());
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
void copyModuleMap(Luau::DenseHashMap<K, V>& result, const Luau::DenseHashMap<K, V>& input)
|
||||
{
|
||||
for (auto [k, v] : input)
|
||||
result[k] = v;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteIfRecommendations)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
template<typename K, typename V>
|
||||
void cloneModuleMap_DEPRECATED(TypeArena& destArena, CloneState& cloneState, const Luau::DenseHashMap<K, V>& source, Luau::DenseHashMap<K, V>& dest)
|
||||
{
|
||||
for (auto [k, v] : source)
|
||||
{
|
||||
dest[k] = Luau::clone(v, destArena, cloneState);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
void cloneModuleMap(
|
||||
TypeArena& destArena,
|
||||
CloneState& cloneState,
|
||||
const Luau::DenseHashMap<K, V>& source,
|
||||
Luau::DenseHashMap<K, V>& dest,
|
||||
Scope* freshScopeForFreeType
|
||||
)
|
||||
{
|
||||
for (auto [k, v] : source)
|
||||
{
|
||||
dest[k] = Luau::cloneIncremental(v, destArena, cloneState, freshScopeForFreeType);
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<size_t, size_t> getDocumentOffsets(std::string_view src, const Position& startPos, const Position& endPos);
|
||||
|
||||
// when typing a function partially, get the span of the first line
|
||||
|
@ -141,6 +100,29 @@ Location getAstStatForExtents(AstStatFor* forStat)
|
|||
return Location{begin, end};
|
||||
}
|
||||
|
||||
// Traverses the linkedlist of else if statements to find the one that matches the cursor position the best
|
||||
AstStatIf* getNearestIfToCursor(AstStat* stmt, const Position& cursorPos)
|
||||
{
|
||||
AstStatIf* current = stmt->as<AstStatIf>();
|
||||
if (!current)
|
||||
return nullptr;
|
||||
while (current->is<AstStatIf>())
|
||||
{
|
||||
if (current->elsebody && current->elsebody->location.containsClosed(cursorPos))
|
||||
{
|
||||
if (auto elseIfS = current->elsebody->as<AstStatIf>())
|
||||
{
|
||||
current = elseIfS;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
Location getFragmentLocation(AstStat* nearestStatement, const Position& cursorPosition)
|
||||
{
|
||||
Location empty{cursorPosition, cursorPosition};
|
||||
|
@ -199,25 +181,59 @@ Location getFragmentLocation(AstStat* nearestStatement, const Position& cursorPo
|
|||
else
|
||||
return empty;
|
||||
}
|
||||
|
||||
if (auto ifS = nearestStatement->as<AstStatIf>())
|
||||
if (FFlag::LuauFragmentAutocompleteIfRecommendations)
|
||||
{
|
||||
auto conditionExtents = Location{ifS->location.begin, ifS->condition->location.end};
|
||||
if (conditionExtents.containsClosed(cursorPosition))
|
||||
return nonEmpty;
|
||||
else if (ifS->thenbody->location.containsClosed(cursorPosition))
|
||||
return empty;
|
||||
else if (auto elseS = ifS->elsebody)
|
||||
if (auto ifS = getNearestIfToCursor(nearestStatement, cursorPosition))
|
||||
{
|
||||
if (auto elseIf = ifS->elsebody->as<AstStatIf>())
|
||||
auto conditionExtents = Location{ifS->condition->location.begin, ifS->condition->location.end};
|
||||
if (conditionExtents.containsClosed(cursorPosition) || !ifS->thenLocation)
|
||||
{
|
||||
|
||||
if (elseIf->thenbody->hasEnd)
|
||||
// CLI-152249 - the condition parse location can sometimes be after the body of the if
|
||||
// statement. This is a bug that results returning locations like {3,0 - 2,0} which is
|
||||
// wrong.
|
||||
if (ifS->condition->location.begin > cursorPosition)
|
||||
return empty;
|
||||
else
|
||||
return {elseS->location.begin, cursorPosition};
|
||||
return Location{ifS->condition->location.begin, cursorPosition};
|
||||
}
|
||||
|
||||
else if (ifS->thenbody->location.containsClosed(cursorPosition))
|
||||
return empty;
|
||||
else if (auto elseS = ifS->elsebody)
|
||||
{
|
||||
if (auto elseIf = ifS->elsebody->as<AstStatIf>())
|
||||
{
|
||||
auto elseIfConditionExtents = Location{elseIf->location.begin, elseIf->condition->location.end};
|
||||
if (elseIfConditionExtents.containsClosed(cursorPosition))
|
||||
return {elseIf->condition->location.begin, cursorPosition};
|
||||
if (elseIf->thenbody->hasEnd)
|
||||
return empty;
|
||||
else
|
||||
return {elseS->location.begin, cursorPosition};
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto ifS = nearestStatement->as<AstStatIf>())
|
||||
{
|
||||
auto conditionExtents = Location{ifS->location.begin, ifS->condition->location.end};
|
||||
if (conditionExtents.containsClosed(cursorPosition))
|
||||
return nonEmpty;
|
||||
else if (ifS->thenbody->location.containsClosed(cursorPosition))
|
||||
return empty;
|
||||
else if (auto elseS = ifS->elsebody)
|
||||
{
|
||||
if (auto elseIf = ifS->elsebody->as<AstStatIf>())
|
||||
{
|
||||
if (elseIf->thenbody->hasEnd)
|
||||
return empty;
|
||||
else
|
||||
return {elseS->location.begin, cursorPosition};
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -504,9 +520,31 @@ std::optional<FragmentParseResult> parseFragment(
|
|||
if (p.root == nullptr)
|
||||
return std::nullopt;
|
||||
|
||||
std::vector<AstNode*> fabricatedAncestry = std::move(result.ancestry);
|
||||
std::vector<AstNode*> fabricatedAncestry;
|
||||
// Moves cannot be on the rhs of a ? : statement, so the assignment is placed in an if else stmt
|
||||
// Make sure to trim this comment after you remove FFlag::LuauFragmentAutocompleteIfRecommendations
|
||||
if (FFlag::LuauFragmentAutocompleteIfRecommendations)
|
||||
fabricatedAncestry = findAncestryAtPositionForAutocomplete(mostRecentParse, cursorPos);
|
||||
else
|
||||
fabricatedAncestry = std::move(result.ancestry);
|
||||
std::vector<AstNode*> fragmentAncestry = findAncestryAtPositionForAutocomplete(p.root, cursorPos);
|
||||
fabricatedAncestry.insert(fabricatedAncestry.end(), fragmentAncestry.begin(), fragmentAncestry.end());
|
||||
|
||||
// Computes the accurate ancestry and then replaces the nodes that correspond to the fragment ancestry
|
||||
// Needed because we look up types by pointer identity
|
||||
if (FFlag::LuauFragmentAutocompleteIfRecommendations)
|
||||
{
|
||||
LUAU_ASSERT(!fabricatedAncestry.empty());
|
||||
auto back = fabricatedAncestry.size() - 1;
|
||||
for (auto it = fragmentAncestry.rbegin(); it != fragmentAncestry.rend(); ++it)
|
||||
{
|
||||
if (back >= 0 && back < fabricatedAncestry.size() && (*it)->classIndex == fabricatedAncestry[back]->classIndex)
|
||||
fabricatedAncestry[back] = *it;
|
||||
back--;
|
||||
}
|
||||
}
|
||||
else
|
||||
fabricatedAncestry.insert(fabricatedAncestry.end(), fragmentAncestry.begin(), fragmentAncestry.end());
|
||||
|
||||
if (nearestStatement == nullptr)
|
||||
nearestStatement = p.root;
|
||||
fragmentResult.root = p.root;
|
||||
|
@ -718,179 +756,6 @@ void cloneTypesFromFragment(
|
|||
destScope->returnType = Luau::cloneIncremental(staleScope->returnType, *destArena, cloneState, destScope);
|
||||
}
|
||||
|
||||
|
||||
struct MixedModeIncrementalTCDefFinder : public AstVisitor
|
||||
{
|
||||
|
||||
bool visit(AstExprLocal* local) override
|
||||
{
|
||||
referencedLocalDefs.emplace_back(local->local, local);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstTypeTypeof* node) override
|
||||
{
|
||||
// We need to traverse typeof expressions because they may refer to locals that we need
|
||||
// to populate the local environment for fragment typechecking. For example, `typeof(m)`
|
||||
// requires that we find the local/global `m` and place it in the environment.
|
||||
// The default behaviour here is to return false, and have individual visitors override
|
||||
// the specific behaviour they need.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatTypeAlias* alias) override
|
||||
{
|
||||
if (FFlag::LuauCloneTypeAliasBindings)
|
||||
declaredAliases.insert(std::string(alias->name.value));
|
||||
return true;
|
||||
}
|
||||
|
||||
// ast defs is just a mapping from expr -> def in general
|
||||
// will get built up by the dfg builder
|
||||
|
||||
// localDefs, we need to copy over
|
||||
std::vector<std::pair<AstLocal*, AstExpr*>> referencedLocalDefs;
|
||||
DenseHashSet<Name> declaredAliases{""};
|
||||
};
|
||||
|
||||
void cloneAndSquashScopes_DEPRECATED(
|
||||
CloneState& cloneState,
|
||||
const Scope* staleScope,
|
||||
const ModulePtr& staleModule,
|
||||
NotNull<TypeArena> destArena,
|
||||
NotNull<DataFlowGraph> dfg,
|
||||
AstStatBlock* program,
|
||||
Scope* destScope
|
||||
)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("Luau::cloneAndSquashScopes", "FragmentAutocomplete");
|
||||
std::vector<const Scope*> scopes;
|
||||
for (const Scope* current = staleScope; current; current = current->parent.get())
|
||||
{
|
||||
scopes.emplace_back(current);
|
||||
}
|
||||
|
||||
// in reverse order (we need to clone the parents and override defs as we go down the list)
|
||||
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it)
|
||||
{
|
||||
const Scope* curr = *it;
|
||||
// Clone the lvalue types
|
||||
for (const auto& [def, ty] : curr->lvalueTypes)
|
||||
destScope->lvalueTypes[def] = Luau::clone(ty, *destArena, cloneState);
|
||||
// Clone the rvalueRefinements
|
||||
for (const auto& [def, ty] : curr->rvalueRefinements)
|
||||
destScope->rvalueRefinements[def] = Luau::clone(ty, *destArena, cloneState);
|
||||
for (const auto& [n, m] : curr->importedTypeBindings)
|
||||
{
|
||||
std::unordered_map<Name, TypeFun> importedBindingTypes;
|
||||
for (const auto& [v, tf] : m)
|
||||
importedBindingTypes[v] = Luau::clone(tf, *destArena, cloneState);
|
||||
destScope->importedTypeBindings[n] = m;
|
||||
}
|
||||
|
||||
// Finally, clone up the bindings
|
||||
for (const auto& [s, b] : curr->bindings)
|
||||
{
|
||||
destScope->bindings[s] = Luau::clone(b, *destArena, cloneState);
|
||||
}
|
||||
}
|
||||
|
||||
// The above code associates defs with TypeId's in the scope
|
||||
// so that lookup to locals will succeed.
|
||||
MixedModeIncrementalTCDefFinder finder;
|
||||
program->visit(&finder);
|
||||
std::vector<std::pair<AstLocal*, AstExpr*>> locals = std::move(finder.referencedLocalDefs);
|
||||
for (auto [loc, expr] : locals)
|
||||
{
|
||||
if (std::optional<Binding> binding = staleScope->linearSearchForBinding(loc->name.value, true))
|
||||
{
|
||||
destScope->lvalueTypes[dfg->getDef(expr)] = Luau::clone(binding->typeId, *destArena, cloneState);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void cloneAndSquashScopes(
|
||||
CloneState& cloneState,
|
||||
const Scope* staleScope,
|
||||
const ModulePtr& staleModule,
|
||||
NotNull<TypeArena> destArena,
|
||||
NotNull<DataFlowGraph> dfg,
|
||||
AstStatBlock* program,
|
||||
Scope* destScope
|
||||
)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("Luau::cloneAndSquashScopes", "FragmentAutocomplete");
|
||||
std::vector<const Scope*> scopes;
|
||||
for (const Scope* current = staleScope; current; current = current->parent.get())
|
||||
{
|
||||
scopes.emplace_back(current);
|
||||
}
|
||||
|
||||
MixedModeIncrementalTCDefFinder finder;
|
||||
|
||||
if (FFlag::LuauCloneTypeAliasBindings)
|
||||
program->visit(&finder);
|
||||
// in reverse order (we need to clone the parents and override defs as we go down the list)
|
||||
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it)
|
||||
{
|
||||
const Scope* curr = *it;
|
||||
// Clone the lvalue types
|
||||
for (const auto& [def, ty] : curr->lvalueTypes)
|
||||
destScope->lvalueTypes[def] = Luau::cloneIncremental(ty, *destArena, cloneState, destScope);
|
||||
// Clone the rvalueRefinements
|
||||
for (const auto& [def, ty] : curr->rvalueRefinements)
|
||||
destScope->rvalueRefinements[def] = Luau::cloneIncremental(ty, *destArena, cloneState, destScope);
|
||||
|
||||
if (FFlag::LuauCloneTypeAliasBindings)
|
||||
{
|
||||
for (const auto& [n, tf] : curr->exportedTypeBindings)
|
||||
{
|
||||
if (!finder.declaredAliases.contains(n))
|
||||
destScope->exportedTypeBindings[n] = Luau::cloneIncremental(tf, *destArena, cloneState, destScope);
|
||||
}
|
||||
|
||||
for (const auto& [n, tf] : curr->privateTypeBindings)
|
||||
{
|
||||
if (!finder.declaredAliases.contains(n))
|
||||
destScope->privateTypeBindings[n] = Luau::cloneIncremental(tf, *destArena, cloneState, destScope);
|
||||
}
|
||||
}
|
||||
for (const auto& [n, m] : curr->importedTypeBindings)
|
||||
{
|
||||
std::unordered_map<Name, TypeFun> importedBindingTypes;
|
||||
for (const auto& [v, tf] : m)
|
||||
importedBindingTypes[v] = Luau::cloneIncremental(tf, *destArena, cloneState, destScope);
|
||||
destScope->importedTypeBindings[n] = std::move(importedBindingTypes);
|
||||
}
|
||||
|
||||
// Finally, clone up the bindings
|
||||
for (const auto& [s, b] : curr->bindings)
|
||||
{
|
||||
destScope->bindings[s] = Luau::cloneIncremental(b, *destArena, cloneState, destScope);
|
||||
}
|
||||
}
|
||||
|
||||
if (!FFlag::LuauCloneTypeAliasBindings)
|
||||
program->visit(&finder);
|
||||
// The above code associates defs with TypeId's in the scope
|
||||
// so that lookup to locals will succeed.
|
||||
|
||||
std::vector<std::pair<AstLocal*, AstExpr*>> locals = std::move(finder.referencedLocalDefs);
|
||||
for (auto [loc, expr] : locals)
|
||||
{
|
||||
if (std::optional<Binding> binding = staleScope->linearSearchForBinding(loc->name.value, true))
|
||||
{
|
||||
destScope->lvalueTypes[dfg->getDef(expr)] = Luau::cloneIncremental(binding->typeId, *destArena, cloneState, destScope);
|
||||
}
|
||||
}
|
||||
|
||||
if (destScope->returnType)
|
||||
destScope->returnType = Luau::cloneIncremental(destScope->returnType, *destArena, cloneState, destScope);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static FrontendModuleResolver& getModuleResolver(Frontend& frontend, std::optional<FrontendOptions> options)
|
||||
{
|
||||
if (FFlag::LuauSolverV2 || !options)
|
||||
|
@ -1031,8 +896,11 @@ static std::pair<size_t, size_t> getDocumentOffsets(std::string_view src, const
|
|||
if (endPos.line == lineCount && endPos.column == colCount)
|
||||
{
|
||||
endOffset = docOffset;
|
||||
while (endOffset < src.size() && src[endOffset] != '\n')
|
||||
endOffset++;
|
||||
if (!FFlag::LuauFragmentAutocompleteIfRecommendations)
|
||||
{
|
||||
while (endOffset < src.size() && src[endOffset] != '\n')
|
||||
endOffset++;
|
||||
}
|
||||
foundEnd = true;
|
||||
}
|
||||
|
||||
|
@ -1177,80 +1045,6 @@ std::optional<FragmentParseResult> parseFragment_DEPRECATED(
|
|||
return fragmentResult;
|
||||
}
|
||||
|
||||
ModulePtr cloneModule_DEPRECATED(CloneState& cloneState, const ModulePtr& source, std::unique_ptr<Allocator> alloc)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("Luau::cloneModule", "FragmentAutocomplete");
|
||||
freeze(source->internalTypes);
|
||||
freeze(source->interfaceTypes);
|
||||
ModulePtr incremental = std::make_shared<Module>();
|
||||
incremental->name = source->name;
|
||||
incremental->humanReadableName = source->humanReadableName;
|
||||
incremental->allocator = std::move(alloc);
|
||||
// Clone types
|
||||
cloneModuleMap_DEPRECATED(incremental->internalTypes, cloneState, source->astTypes, incremental->astTypes);
|
||||
cloneModuleMap_DEPRECATED(incremental->internalTypes, cloneState, source->astTypePacks, incremental->astTypePacks);
|
||||
cloneModuleMap_DEPRECATED(incremental->internalTypes, cloneState, source->astExpectedTypes, incremental->astExpectedTypes);
|
||||
|
||||
cloneModuleMap_DEPRECATED(incremental->internalTypes, cloneState, source->astOverloadResolvedTypes, incremental->astOverloadResolvedTypes);
|
||||
|
||||
cloneModuleMap_DEPRECATED(incremental->internalTypes, cloneState, source->astForInNextTypes, incremental->astForInNextTypes);
|
||||
|
||||
copyModuleMap(incremental->astScopes, source->astScopes);
|
||||
|
||||
return incremental;
|
||||
}
|
||||
|
||||
ModulePtr cloneModule(CloneState& cloneState, const ModulePtr& source, std::unique_ptr<Allocator> alloc, Scope* freeTypeFreshScope)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("Luau::cloneModule", "FragmentAutocomplete");
|
||||
freeze(source->internalTypes);
|
||||
freeze(source->interfaceTypes);
|
||||
ModulePtr incremental = std::make_shared<Module>();
|
||||
incremental->name = source->name;
|
||||
incremental->humanReadableName = source->humanReadableName;
|
||||
incremental->allocator = std::move(alloc);
|
||||
// Clone types
|
||||
cloneModuleMap(incremental->internalTypes, cloneState, source->astTypes, incremental->astTypes, freeTypeFreshScope);
|
||||
cloneModuleMap(incremental->internalTypes, cloneState, source->astTypePacks, incremental->astTypePacks, freeTypeFreshScope);
|
||||
cloneModuleMap(incremental->internalTypes, cloneState, source->astExpectedTypes, incremental->astExpectedTypes, freeTypeFreshScope);
|
||||
|
||||
cloneModuleMap(
|
||||
incremental->internalTypes, cloneState, source->astOverloadResolvedTypes, incremental->astOverloadResolvedTypes, freeTypeFreshScope
|
||||
);
|
||||
|
||||
cloneModuleMap(incremental->internalTypes, cloneState, source->astForInNextTypes, incremental->astForInNextTypes, freeTypeFreshScope);
|
||||
|
||||
copyModuleMap(incremental->astScopes, source->astScopes);
|
||||
|
||||
return incremental;
|
||||
}
|
||||
|
||||
void mixedModeCompatibility(
|
||||
const ScopePtr& bottomScopeStale,
|
||||
const ScopePtr& myFakeScope,
|
||||
const ModulePtr& stale,
|
||||
NotNull<DataFlowGraph> dfg,
|
||||
AstStatBlock* program
|
||||
)
|
||||
{
|
||||
// This code does the following
|
||||
// traverse program
|
||||
// look for ast refs for locals
|
||||
// ask for the corresponding defId from dfg
|
||||
// given that defId, and that expression, in the incremental module, map lvalue types from defID to
|
||||
|
||||
MixedModeIncrementalTCDefFinder finder;
|
||||
program->visit(&finder);
|
||||
std::vector<std::pair<AstLocal*, AstExpr*>> locals = std::move(finder.referencedLocalDefs);
|
||||
for (auto [loc, expr] : locals)
|
||||
{
|
||||
if (std::optional<Binding> binding = bottomScopeStale->linearSearchForBinding(loc->name.value, true))
|
||||
{
|
||||
myFakeScope->lvalueTypes[dfg->getDef(expr)] = binding->typeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reportWaypoint(IFragmentAutocompleteReporter* reporter, FragmentAutocompleteWaypoint type)
|
||||
{
|
||||
if (!reporter)
|
||||
|
@ -1267,156 +1061,6 @@ static void reportFragmentString(IFragmentAutocompleteReporter* reporter, std::s
|
|||
reporter->reportFragmentString(fragment);
|
||||
}
|
||||
|
||||
FragmentTypeCheckResult typecheckFragmentHelper_DEPRECATED(
|
||||
Frontend& frontend,
|
||||
AstStatBlock* root,
|
||||
const ModulePtr& stale,
|
||||
const ScopePtr& closestScope,
|
||||
const Position& cursorPos,
|
||||
std::unique_ptr<Allocator> astAllocator,
|
||||
const FrontendOptions& opts,
|
||||
IFragmentAutocompleteReporter* reporter
|
||||
)
|
||||
{
|
||||
LUAU_TIMETRACE_SCOPE("Luau::typecheckFragment_", "FragmentAutocomplete");
|
||||
|
||||
freeze(stale->internalTypes);
|
||||
freeze(stale->interfaceTypes);
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneModuleStart);
|
||||
CloneState cloneState{frontend.builtinTypes};
|
||||
std::shared_ptr<Scope> freshChildOfNearestScope = std::make_shared<Scope>(closestScope);
|
||||
ModulePtr incrementalModule = nullptr;
|
||||
if (FFlag::LuauAllFreeTypesHaveScopes)
|
||||
incrementalModule = cloneModule(cloneState, stale, std::move(astAllocator), freshChildOfNearestScope.get());
|
||||
else
|
||||
incrementalModule = cloneModule_DEPRECATED(cloneState, stale, std::move(astAllocator));
|
||||
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneModuleEnd);
|
||||
incrementalModule->checkedInNewSolver = true;
|
||||
unfreeze(incrementalModule->internalTypes);
|
||||
unfreeze(incrementalModule->interfaceTypes);
|
||||
|
||||
/// Setup typecheck limits
|
||||
TypeCheckLimits limits;
|
||||
if (opts.moduleTimeLimitSec)
|
||||
limits.finishTime = TimeTrace::getClock() + *opts.moduleTimeLimitSec;
|
||||
else
|
||||
limits.finishTime = std::nullopt;
|
||||
limits.cancellationToken = opts.cancellationToken;
|
||||
|
||||
/// Icehandler
|
||||
NotNull<InternalErrorReporter> iceHandler{&frontend.iceHandler};
|
||||
/// Make the shared state for the unifier (recursion + iteration limits)
|
||||
UnifierSharedState unifierState{iceHandler};
|
||||
unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit;
|
||||
unifierState.counters.iterationLimit = limits.unifierIterationLimit.value_or(FInt::LuauTypeInferIterationLimit);
|
||||
|
||||
/// Initialize the normalizer
|
||||
Normalizer normalizer{&incrementalModule->internalTypes, frontend.builtinTypes, NotNull{&unifierState}};
|
||||
|
||||
/// User defined type functions runtime
|
||||
TypeFunctionRuntime typeFunctionRuntime(iceHandler, NotNull{&limits});
|
||||
|
||||
/// Create a DataFlowGraph just for the surrounding context
|
||||
DataFlowGraph dfg = DataFlowGraphBuilder::build(root, NotNull{&incrementalModule->defArena}, NotNull{&incrementalModule->keyArena}, iceHandler);
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::DfgBuildEnd);
|
||||
|
||||
SimplifierPtr simplifier = newSimplifier(NotNull{&incrementalModule->internalTypes}, frontend.builtinTypes);
|
||||
|
||||
FrontendModuleResolver& resolver = getModuleResolver(frontend, opts);
|
||||
|
||||
/// Contraint Generator
|
||||
ConstraintGenerator cg{
|
||||
incrementalModule,
|
||||
NotNull{&normalizer},
|
||||
NotNull{simplifier.get()},
|
||||
NotNull{&typeFunctionRuntime},
|
||||
NotNull{&resolver},
|
||||
frontend.builtinTypes,
|
||||
iceHandler,
|
||||
stale->getModuleScope(),
|
||||
frontend.globals.globalTypeFunctionScope,
|
||||
nullptr,
|
||||
nullptr,
|
||||
NotNull{&dfg},
|
||||
{}
|
||||
};
|
||||
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeStart);
|
||||
incrementalModule->scopes.emplace_back(root->location, freshChildOfNearestScope);
|
||||
cg.rootScope = freshChildOfNearestScope.get();
|
||||
|
||||
if (FFlag::LuauAllFreeTypesHaveScopes)
|
||||
cloneAndSquashScopes(
|
||||
cloneState, closestScope.get(), stale, NotNull{&incrementalModule->internalTypes}, NotNull{&dfg}, root, freshChildOfNearestScope.get()
|
||||
);
|
||||
else
|
||||
cloneAndSquashScopes_DEPRECATED(
|
||||
cloneState, closestScope.get(), stale, NotNull{&incrementalModule->internalTypes}, NotNull{&dfg}, root, freshChildOfNearestScope.get()
|
||||
);
|
||||
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::CloneAndSquashScopeEnd);
|
||||
cg.visitFragmentRoot(freshChildOfNearestScope, root);
|
||||
|
||||
if (FFlag::LuauPersistConstraintGenerationScopes)
|
||||
{
|
||||
for (auto p : cg.scopes)
|
||||
incrementalModule->scopes.emplace_back(std::move(p));
|
||||
}
|
||||
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverStart);
|
||||
|
||||
if (FFlag::LuauAllFreeTypesHaveScopes)
|
||||
{
|
||||
if (Scope* sc = freshChildOfNearestScope.get())
|
||||
{
|
||||
if (!sc->interiorFreeTypes.has_value())
|
||||
sc->interiorFreeTypes.emplace();
|
||||
if (!sc->interiorFreeTypePacks.has_value())
|
||||
sc->interiorFreeTypePacks.emplace();
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the constraint solver and run it
|
||||
ConstraintSolver cs{
|
||||
NotNull{&normalizer},
|
||||
NotNull{simplifier.get()},
|
||||
NotNull{&typeFunctionRuntime},
|
||||
NotNull(cg.rootScope),
|
||||
borrowConstraints(cg.constraints),
|
||||
NotNull{&cg.scopeToFunction},
|
||||
incrementalModule->name,
|
||||
NotNull{&resolver},
|
||||
{},
|
||||
nullptr,
|
||||
NotNull{&dfg},
|
||||
limits
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
cs.run();
|
||||
}
|
||||
catch (const TimeLimitError&)
|
||||
{
|
||||
stale->timeout = true;
|
||||
}
|
||||
catch (const UserCancelError&)
|
||||
{
|
||||
stale->cancelled = true;
|
||||
}
|
||||
|
||||
reportWaypoint(reporter, FragmentAutocompleteWaypoint::ConstraintSolverEnd);
|
||||
|
||||
// In frontend we would forbid internal types
|
||||
// because this is just for autocomplete, we don't actually care
|
||||
// We also don't even need to typecheck - just synthesize types as best as we can
|
||||
|
||||
freeze(incrementalModule->internalTypes);
|
||||
freeze(incrementalModule->interfaceTypes);
|
||||
return {std::move(incrementalModule), std::move(freshChildOfNearestScope)};
|
||||
}
|
||||
|
||||
FragmentTypeCheckResult typecheckFragment_(
|
||||
Frontend& frontend,
|
||||
AstStatBlock* root,
|
||||
|
@ -1609,12 +1253,7 @@ std::pair<FragmentTypeCheckStatus, FragmentTypeCheckResult> typecheckFragment(
|
|||
FrontendOptions frontendOptions = opts.value_or(frontend.options);
|
||||
const ScopePtr& closestScope = FFlag::LuauBetterScopeSelection ? findClosestScope(module, parseResult.scopePos)
|
||||
: findClosestScope_DEPRECATED(module, parseResult.nearestStatement);
|
||||
FragmentTypeCheckResult result =
|
||||
FFlag::LuauIncrementalAutocompleteDemandBasedCloning
|
||||
? typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter)
|
||||
: typecheckFragmentHelper_DEPRECATED(
|
||||
frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter
|
||||
);
|
||||
FragmentTypeCheckResult result = typecheckFragment_(frontend, parseResult.root, module, closestScope, cursorPos, std::move(parseResult.alloc), frontendOptions, reporter);
|
||||
result.ancestry = std::move(parseResult.ancestry);
|
||||
reportFragmentString(reporter, tryParse->fragmentToParse);
|
||||
return {FragmentTypeCheckStatus::Success, result};
|
||||
|
|
|
@ -1627,6 +1627,10 @@ void pruneUnnecessaryGenerics(
|
|||
return true;
|
||||
seen.insert(ty);
|
||||
|
||||
auto state = counter.generics.find(ty);
|
||||
if (state && state->count == 0)
|
||||
return true;
|
||||
|
||||
return !get<GenericType>(ty);
|
||||
}
|
||||
);
|
||||
|
@ -1650,6 +1654,10 @@ void pruneUnnecessaryGenerics(
|
|||
return true;
|
||||
seen2.insert(tp);
|
||||
|
||||
auto state = counter.genericPacks.find(tp);
|
||||
if (state && state->count == 0)
|
||||
return true;
|
||||
|
||||
return !get<GenericTypePack>(tp);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -19,7 +19,6 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTFLAG(LuauAttribute)
|
||||
LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute)
|
||||
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
|
@ -2295,53 +2294,36 @@ private:
|
|||
|
||||
bool visit(AstExprLocal* node) override
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
|
||||
const FunctionType* fty = getFunctionType(node);
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
||||
if (shouldReport)
|
||||
report(node->location, node->local->name.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstExprGlobal* node) override
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
const FunctionType* fty = getFunctionType(node);
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
||||
if (shouldReport)
|
||||
report(node->location, node->name.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatLocalFunction* node) override
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
check(node->func);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstStatFunction* node) override
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
{
|
||||
check(node->func);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(AstExprIndexName* node) override
|
||||
|
@ -2385,13 +2367,13 @@ private:
|
|||
{
|
||||
if (const ExternType* cty = get<ExternType>(ty))
|
||||
{
|
||||
const Property* prop = lookupExternTypeProp(cty, node->index.value);
|
||||
|
||||
if (prop && prop->deprecated)
|
||||
report(node->location, *prop, cty->name.c_str(), node->index.value);
|
||||
else if (FFlag::LuauDeprecatedAttribute && prop)
|
||||
if (const Property* prop = lookupExternTypeProp(cty, node->index.value))
|
||||
{
|
||||
if (std::optional<TypeId> ty = prop->readTy)
|
||||
if (prop->deprecated)
|
||||
{
|
||||
report(node->location, *prop, cty->name.c_str(), node->index.value);
|
||||
}
|
||||
else if (std::optional<TypeId> ty = prop->readTy)
|
||||
{
|
||||
const FunctionType* fty = get<FunctionType>(follow(ty));
|
||||
bool shouldReport = fty && fty->isDeprecatedFunction && !inScope(fty);
|
||||
|
@ -2423,7 +2405,7 @@ private:
|
|||
else
|
||||
report(node->location, prop->second, tty->name ? tty->name->c_str() : nullptr, node->index.value);
|
||||
}
|
||||
else if (FFlag::LuauDeprecatedAttribute)
|
||||
else
|
||||
{
|
||||
if (std::optional<TypeId> ty = prop->second.readTy)
|
||||
{
|
||||
|
@ -2462,7 +2444,6 @@ private:
|
|||
|
||||
void check(AstExprFunction* func)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
LUAU_ASSERT(func);
|
||||
|
||||
const FunctionType* fty = getFunctionType(func);
|
||||
|
@ -2492,8 +2473,6 @@ private:
|
|||
|
||||
void report(const Location& location, const char* tableName, const char* functionName)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
if (tableName)
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s.%s' is deprecated", tableName, functionName);
|
||||
else
|
||||
|
@ -2502,8 +2481,6 @@ private:
|
|||
|
||||
void report(const Location& location, const char* functionName)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Function '%s' is deprecated", functionName);
|
||||
}
|
||||
|
||||
|
@ -2511,7 +2488,6 @@ private:
|
|||
|
||||
void pushScope(const FunctionType* fty)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
LUAU_ASSERT(fty);
|
||||
|
||||
functionTypeScopeStack.push_back(fty);
|
||||
|
@ -2519,7 +2495,6 @@ private:
|
|||
|
||||
void popScope(const FunctionType* fty)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
LUAU_ASSERT(fty);
|
||||
|
||||
LUAU_ASSERT(fty == functionTypeScopeStack.back());
|
||||
|
@ -2528,7 +2503,6 @@ private:
|
|||
|
||||
bool inScope(const FunctionType* fty) const
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
LUAU_ASSERT(fty);
|
||||
|
||||
return std::find(functionTypeScopeStack.begin(), functionTypeScopeStack.end(), fty) != functionTypeScopeStack.end();
|
||||
|
@ -2536,8 +2510,6 @@ private:
|
|||
|
||||
const FunctionType* getFunctionType(AstExpr* node)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
std::optional<TypeId> ty = context->getType(node);
|
||||
if (!ty)
|
||||
return nullptr;
|
||||
|
|
|
@ -11,7 +11,6 @@ LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256)
|
||||
LUAU_FASTFLAG(LuauSyntheticErrors)
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -101,8 +100,7 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
|
|||
clone.tags = a.tags;
|
||||
clone.argNames = a.argNames;
|
||||
clone.isCheckedFunction = a.isCheckedFunction;
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
clone.isDeprecatedFunction = a.isDeprecatedFunction;
|
||||
clone.isDeprecatedFunction = a.isDeprecatedFunction;
|
||||
return dest.addType(std::move(clone));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, TableType>)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
LUAU_FASTFLAGVARIABLE(DebugLuauSubtypingCheckPathValidity)
|
||||
LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubtypingEnableReasoningLimit)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSubtypeGenericsAndNegations)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -669,6 +670,18 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
result = {false};
|
||||
else if (get<ErrorType>(subTy))
|
||||
result = {true};
|
||||
else if (auto subGeneric = get<GenericType>(subTy); FFlag::LuauSubtypeGenericsAndNegations && subGeneric && variance == Variance::Covariant)
|
||||
{
|
||||
bool ok = bindGeneric(env, subTy, superTy);
|
||||
result.isSubtype = ok;
|
||||
result.isCacheable = false;
|
||||
}
|
||||
else if (auto superGeneric = get<GenericType>(superTy); FFlag::LuauSubtypeGenericsAndNegations && superGeneric && variance == Variance::Contravariant)
|
||||
{
|
||||
bool ok = bindGeneric(env, subTy, superTy);
|
||||
result.isSubtype = ok;
|
||||
result.isCacheable = false;
|
||||
}
|
||||
else if (auto p = get2<NegationType, NegationType>(subTy, superTy))
|
||||
result = isCovariantWith(env, p.first->ty, p.second->ty, scope).withBothComponent(TypePath::TypeField::Negated);
|
||||
else if (auto subNegation = get<NegationType>(subTy))
|
||||
|
@ -711,13 +724,13 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, TypeId sub
|
|||
|
||||
result = isCovariantWith(env, subTy, superTypeFunctionInstance, scope);
|
||||
}
|
||||
else if (auto subGeneric = get<GenericType>(subTy); subGeneric && variance == Variance::Covariant)
|
||||
else if (auto subGeneric = get<GenericType>(subTy); !FFlag::LuauSubtypeGenericsAndNegations && subGeneric && variance == Variance::Covariant)
|
||||
{
|
||||
bool ok = bindGeneric(env, subTy, superTy);
|
||||
result.isSubtype = ok;
|
||||
result.isCacheable = false;
|
||||
}
|
||||
else if (auto superGeneric = get<GenericType>(superTy); superGeneric && variance == Variance::Contravariant)
|
||||
else if (auto superGeneric = get<GenericType>(superTy); !FFlag::LuauSubtypeGenericsAndNegations && superGeneric && variance == Variance::Contravariant)
|
||||
{
|
||||
bool ok = bindGeneric(env, subTy, superTy);
|
||||
result.isSubtype = ok;
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include "Luau/TypeUtils.h"
|
||||
#include "Luau/Unifier2.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalFailsafe)
|
||||
LUAU_FASTFLAGVARIABLE(LuauBidirectionalInferenceElideAssert)
|
||||
|
||||
namespace Luau
|
||||
|
@ -148,22 +146,13 @@ TypeId matchLiteralType(
|
|||
expectedType = follow(expectedType);
|
||||
exprType = follow(exprType);
|
||||
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
{
|
||||
// The intent of `matchLiteralType` is to upcast values when it's safe
|
||||
// to do so. it's always safe to upcast to `any` or `unknown`, so we
|
||||
// can unconditionally do so here.
|
||||
if (is<AnyType, UnknownType>(expectedType))
|
||||
return expectedType;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (get<AnyType>(expectedType) || get<UnknownType>(expectedType))
|
||||
{
|
||||
// "Narrowing" to unknown or any is not going to do anything useful.
|
||||
return exprType;
|
||||
}
|
||||
}
|
||||
|
||||
// The intent of `matchLiteralType` is to upcast values when it's safe
|
||||
// to do so. it's always safe to upcast to `any` or `unknown`, so we
|
||||
// can unconditionally do so here.
|
||||
if (is<AnyType, UnknownType>(expectedType))
|
||||
return expectedType;
|
||||
|
||||
|
||||
|
||||
if (expr->is<AstExprConstantString>())
|
||||
|
@ -250,7 +239,7 @@ TypeId matchLiteralType(
|
|||
// { x = {}, x = 42 }
|
||||
//
|
||||
// The type of this will be `{ x: number }`
|
||||
if (FFlag::LuauBidirectionalFailsafe && !tableTy)
|
||||
if (!tableTy)
|
||||
return exprType;
|
||||
|
||||
LUAU_ASSERT(tableTy);
|
||||
|
@ -291,7 +280,7 @@ TypeId matchLiteralType(
|
|||
auto it = tableTy->props.find(keyStr);
|
||||
|
||||
// This can occur, potentially, if we are re-entrant.
|
||||
if (FFlag::LuauBidirectionalFailsafe && it == tableTy->props.end())
|
||||
if (it == tableTy->props.end())
|
||||
continue;
|
||||
|
||||
LUAU_ASSERT(it != tableTy->props.end());
|
||||
|
@ -330,18 +319,8 @@ TypeId matchLiteralType(
|
|||
toBlock
|
||||
);
|
||||
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
{
|
||||
indexerKeyTypes.insert(arena->addType(SingletonType{StringSingleton{keyStr}}));
|
||||
indexerValueTypes.insert(matchedType);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tableTy->indexer)
|
||||
unifier->unify(matchedType, tableTy->indexer->indexResultType);
|
||||
else
|
||||
tableTy->indexer = TableIndexer{expectedTableTy->indexer->indexType, matchedType};
|
||||
}
|
||||
indexerKeyTypes.insert(arena->addType(SingletonType{StringSingleton{keyStr}}));
|
||||
indexerValueTypes.insert(matchedType);
|
||||
|
||||
keysToDelete.insert(item.key->as<AstExprConstantString>());
|
||||
}
|
||||
|
@ -409,7 +388,7 @@ TypeId matchLiteralType(
|
|||
}
|
||||
else if (item.kind == AstExprTable::Item::List)
|
||||
{
|
||||
if (!FFlag::LuauBidirectionalInferenceCollectIndexerTypes || !FFlag::LuauBidirectionalInferenceElideAssert)
|
||||
if (!FFlag::LuauBidirectionalInferenceElideAssert)
|
||||
LUAU_ASSERT(tableTy->indexer);
|
||||
|
||||
if (expectedTableTy->indexer)
|
||||
|
@ -431,17 +410,8 @@ TypeId matchLiteralType(
|
|||
toBlock
|
||||
);
|
||||
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
{
|
||||
indexerKeyTypes.insert(builtinTypes->numberType);
|
||||
indexerValueTypes.insert(matchedType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the index result type is the prop type, we can replace it with the matched type here.
|
||||
if (tableTy->indexer->indexResultType == *propTy)
|
||||
tableTy->indexer->indexResultType = matchedType;
|
||||
}
|
||||
indexerKeyTypes.insert(builtinTypes->numberType);
|
||||
indexerValueTypes.insert(matchedType);
|
||||
}
|
||||
}
|
||||
else if (item.kind == AstExprTable::Item::General)
|
||||
|
@ -464,11 +434,8 @@ TypeId matchLiteralType(
|
|||
if (!item.key->as<AstExprConstantString>() && expectedTableTy->indexer)
|
||||
(*astExpectedTypes)[item.key] = expectedTableTy->indexer->indexType;
|
||||
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
{
|
||||
indexerKeyTypes.insert(tKey);
|
||||
indexerValueTypes.insert(tProp);
|
||||
}
|
||||
indexerKeyTypes.insert(tKey);
|
||||
indexerValueTypes.insert(tProp);
|
||||
}
|
||||
else
|
||||
LUAU_ASSERT(!"Unexpected");
|
||||
|
@ -530,7 +497,7 @@ TypeId matchLiteralType(
|
|||
// have one too.
|
||||
// TODO: If the expected table also has an indexer, we might want to
|
||||
// push the expected indexer's types into it.
|
||||
if (FFlag::LuauBidirectionalInferenceCollectIndexerTypes && expectedTableTy->indexer)
|
||||
if (expectedTableTy->indexer)
|
||||
{
|
||||
if (indexerValueTypes.size() > 0 && indexerKeyTypes.size() > 0)
|
||||
{
|
||||
|
|
|
@ -11,10 +11,9 @@
|
|||
#include <math.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -368,7 +367,7 @@ struct Printer_DEPRECATED
|
|||
else if (typeCount == 1)
|
||||
{
|
||||
bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is<AstTypeGroup>());
|
||||
if (FFlag::LuauAstTypeGroup3 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
if (shouldParenthesize)
|
||||
writer.symbol("(");
|
||||
|
||||
// Only variadic tail
|
||||
|
@ -381,7 +380,7 @@ struct Printer_DEPRECATED
|
|||
visualizeTypeAnnotation(*list.types.data[0]);
|
||||
}
|
||||
|
||||
if (FFlag::LuauAstTypeGroup3 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
if (shouldParenthesize)
|
||||
writer.symbol(")");
|
||||
}
|
||||
else
|
||||
|
@ -1233,18 +1232,9 @@ struct Printer_DEPRECATED
|
|||
AstType* l = a->types.data[0];
|
||||
AstType* r = a->types.data[1];
|
||||
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil" && !r->is<AstTypeOptional>())
|
||||
std::swap(l, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil")
|
||||
std::swap(l, r);
|
||||
}
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil" && !r->is<AstTypeOptional>())
|
||||
std::swap(l, r);
|
||||
|
||||
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
|
||||
auto rta = r->as<AstTypeReference>();
|
||||
|
@ -1267,13 +1257,10 @@ struct Printer_DEPRECATED
|
|||
|
||||
for (size_t i = 0; i < a->types.size; ++i)
|
||||
{
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
if (a->types.data[i]->is<AstTypeOptional>())
|
||||
{
|
||||
if (a->types.data[i]->is<AstTypeOptional>())
|
||||
{
|
||||
writer.symbol("?");
|
||||
continue;
|
||||
}
|
||||
writer.symbol("?");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
|
@ -1359,14 +1346,15 @@ struct Printer
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void visualize(const AstLocal& local)
|
||||
void visualize(const AstLocal& local, Position colonPosition)
|
||||
{
|
||||
advance(local.location.begin);
|
||||
|
||||
writer.identifier(local.name.value);
|
||||
if (writeTypes && local.annotation)
|
||||
{
|
||||
// TODO: handle spacing for type annotation
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
advance(colonPosition);
|
||||
writer.symbol(":");
|
||||
visualizeTypeAnnotation(*local.annotation);
|
||||
}
|
||||
|
@ -1428,7 +1416,7 @@ struct Printer
|
|||
else if (typeCount == 1)
|
||||
{
|
||||
bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is<AstTypeGroup>());
|
||||
if (FFlag::LuauAstTypeGroup3 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
if (shouldParenthesize)
|
||||
{
|
||||
if (openParenthesesPosition)
|
||||
advance(*openParenthesesPosition);
|
||||
|
@ -1447,7 +1435,7 @@ struct Printer
|
|||
visualizeTypeAnnotation(*list.types.data[0]);
|
||||
}
|
||||
|
||||
if (FFlag::LuauAstTypeGroup3 ? shouldParenthesize : unconditionallyParenthesize)
|
||||
if (shouldParenthesize)
|
||||
{
|
||||
if (closeParenthesesPosition)
|
||||
advance(*closeParenthesesPosition);
|
||||
|
@ -1973,10 +1961,16 @@ struct Printer
|
|||
writer.keyword("local");
|
||||
|
||||
CommaSeparatorInserter varComma(writer, cstNode ? cstNode->varsCommaPositions.begin() : nullptr);
|
||||
for (const auto& local : a->vars)
|
||||
for (size_t i = 0; i < a->vars.size; i++)
|
||||
{
|
||||
varComma();
|
||||
visualize(*local);
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions && cstNode)
|
||||
{
|
||||
LUAU_ASSERT(cstNode->varsAnnotationColonPositions.size > i);
|
||||
visualize(*a->vars.data[i], cstNode->varsAnnotationColonPositions.data[i]);
|
||||
}
|
||||
else
|
||||
visualize(*a->vars.data[i], Position{0, 0});
|
||||
}
|
||||
|
||||
if (a->equalsSignLocation)
|
||||
|
@ -1999,7 +1993,11 @@ struct Printer
|
|||
|
||||
writer.keyword("for");
|
||||
|
||||
visualize(*a->var);
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
visualize(*a->var, cstNode ? cstNode->annotationColonPosition : Position{0, 0});
|
||||
else
|
||||
visualize(*a->var, Position{0,0});
|
||||
|
||||
if (cstNode)
|
||||
advance(cstNode->equalsPosition);
|
||||
writer.symbol("=");
|
||||
|
@ -2029,10 +2027,16 @@ struct Printer
|
|||
writer.keyword("for");
|
||||
|
||||
CommaSeparatorInserter varComma(writer, cstNode ? cstNode->varsCommaPositions.begin() : nullptr);
|
||||
for (const auto& var : a->vars)
|
||||
for (size_t i = 0; i < a->vars.size; i++)
|
||||
{
|
||||
varComma();
|
||||
visualize(*var);
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions && cstNode)
|
||||
{
|
||||
LUAU_ASSERT(cstNode->varsAnnotationColonPositions.size > i);
|
||||
visualize(*a->vars.data[i], cstNode->varsAnnotationColonPositions.data[i]);
|
||||
}
|
||||
else
|
||||
visualize(*a->vars.data[i], Position{0, 0});
|
||||
}
|
||||
|
||||
advance(a->inLocation.begin);
|
||||
|
@ -2363,6 +2367,11 @@ struct Printer
|
|||
writer.identifier(local->name.value);
|
||||
if (writeTypes && local->annotation)
|
||||
{
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst && FFlag::LuauStoreLocalAnnotationColonPositions && cstNode)
|
||||
{
|
||||
LUAU_ASSERT(cstNode->argsAnnotationColonPositions.size > i);
|
||||
advance(cstNode->argsAnnotationColonPositions.data[i]);
|
||||
}
|
||||
writer.symbol(":");
|
||||
visualizeTypeAnnotation(*local->annotation);
|
||||
}
|
||||
|
@ -2376,6 +2385,11 @@ struct Printer
|
|||
|
||||
if (func.varargAnnotation)
|
||||
{
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst && FFlag::LuauStoreLocalAnnotationColonPositions && cstNode)
|
||||
{
|
||||
LUAU_ASSERT(cstNode->varargAnnotationColonPosition != Position({0, 0}));
|
||||
advance(cstNode->varargAnnotationColonPosition);
|
||||
}
|
||||
writer.symbol(":");
|
||||
visualizeTypePackAnnotation(*func.varargAnnotation, true);
|
||||
}
|
||||
|
@ -2755,18 +2769,9 @@ struct Printer
|
|||
AstType* l = a->types.data[0];
|
||||
AstType* r = a->types.data[1];
|
||||
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil" && !r->is<AstTypeOptional>())
|
||||
std::swap(l, r);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil")
|
||||
std::swap(l, r);
|
||||
}
|
||||
auto lta = l->as<AstTypeReference>();
|
||||
if (lta && lta->name == "nil" && !r->is<AstTypeOptional>())
|
||||
std::swap(l, r);
|
||||
|
||||
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
|
||||
auto rta = r->as<AstTypeReference>();
|
||||
|
@ -2796,21 +2801,17 @@ struct Printer
|
|||
size_t separatorIndex = 0;
|
||||
for (size_t i = 0; i < a->types.size; ++i)
|
||||
{
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
if (const auto optional = a->types.data[i]->as<AstTypeOptional>())
|
||||
{
|
||||
if (const auto optional = a->types.data[i]->as<AstTypeOptional>())
|
||||
{
|
||||
advance(optional->location.begin);
|
||||
writer.symbol("?");
|
||||
continue;
|
||||
}
|
||||
advance(optional->location.begin);
|
||||
writer.symbol("?");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
if (cstNode && FFlag::LuauParseOptionalAsNode2)
|
||||
if (cstNode)
|
||||
{
|
||||
// separatorIndex is only valid if `?` is handled as an AstTypeOptional
|
||||
advance(cstNode->separatorPositions.data[separatorIndex]);
|
||||
separatorIndex++;
|
||||
}
|
||||
|
|
|
@ -30,11 +30,11 @@
|
|||
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerAcceptNumberConcats)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -1323,8 +1323,21 @@ void TypeChecker2::visit(AstExprConstantBool* expr)
|
|||
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
||||
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope);
|
||||
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
if (FFlag::LuauReportSubtypingErrors)
|
||||
{
|
||||
if (!isErrorSuppressing(expr->location, inferredType))
|
||||
{
|
||||
if (!r.isSubtype)
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
|
||||
reportErrors(r.errors);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstExprConstantNumber* expr)
|
||||
|
@ -1348,8 +1361,21 @@ void TypeChecker2::visit(AstExprConstantString* expr)
|
|||
NotNull<Scope> scope{findInnermostScope(expr->location)};
|
||||
|
||||
const SubtypingResult r = subtyping->isSubtype(bestType, inferredType, scope);
|
||||
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
if (FFlag::LuauReportSubtypingErrors)
|
||||
{
|
||||
if (!isErrorSuppressing(expr->location, inferredType))
|
||||
{
|
||||
if (!r.isSubtype)
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
|
||||
reportErrors(r.errors);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!r.isSubtype && !isErrorSuppressing(expr->location, inferredType))
|
||||
reportError(TypeMismatch{inferredType, bestType}, expr->location);
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker2::visit(AstExprLocal* expr)
|
||||
|
@ -1417,6 +1443,12 @@ void TypeChecker2::visitCall(AstExprCall* call)
|
|||
if (result.isSubtype)
|
||||
fnTy = follow(*selectedOverloadTy);
|
||||
|
||||
if (FFlag::LuauReportSubtypingErrors)
|
||||
{
|
||||
if (!isErrorSuppressing(call->location, *selectedOverloadTy))
|
||||
reportErrors(std::move(result.errors));
|
||||
}
|
||||
|
||||
if (result.normalizationTooComplex)
|
||||
{
|
||||
reportError(NormalizationTooComplex{}, call->func->location);
|
||||
|
@ -2728,61 +2760,41 @@ Reasonings TypeChecker2::explainReasonings_(TID subTy, TID superTy, Location loc
|
|||
if (!subLeafTy && !superLeafTy && !subLeafTp && !superLeafTp)
|
||||
ice->ice("Subtyping test returned a reasoning where one path ends at a type and the other ends at a pack.", location);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
std::string relation = "a subtype of";
|
||||
if (reasoning.variance == SubtypingVariance::Invariant)
|
||||
relation = "exactly";
|
||||
else if (reasoning.variance == SubtypingVariance::Contravariant)
|
||||
relation = "a supertype of";
|
||||
std::string relation = "a subtype of";
|
||||
if (reasoning.variance == SubtypingVariance::Invariant)
|
||||
relation = "exactly";
|
||||
else if (reasoning.variance == SubtypingVariance::Contravariant)
|
||||
relation = "a supertype of";
|
||||
|
||||
std::string subLeafAsString = toString(subLeaf);
|
||||
// if the string is empty, it must be an empty type pack
|
||||
if (subLeafAsString.empty())
|
||||
subLeafAsString = "()";
|
||||
std::string subLeafAsString = toString(subLeaf);
|
||||
// if the string is empty, it must be an empty type pack
|
||||
if (subLeafAsString.empty())
|
||||
subLeafAsString = "()";
|
||||
|
||||
std::string superLeafAsString = toString(superLeaf);
|
||||
// if the string is empty, it must be an empty type pack
|
||||
if (superLeafAsString.empty())
|
||||
superLeafAsString = "()";
|
||||
std::string superLeafAsString = toString(superLeaf);
|
||||
// if the string is empty, it must be an empty type pack
|
||||
if (superLeafAsString.empty())
|
||||
superLeafAsString = "()";
|
||||
|
||||
std::stringstream baseReasonBuilder;
|
||||
baseReasonBuilder << "`" << subLeafAsString << "` is not " << relation << " `" << superLeafAsString << "`";
|
||||
std::string baseReason = baseReasonBuilder.str();
|
||||
std::stringstream baseReasonBuilder;
|
||||
baseReasonBuilder << "`" << subLeafAsString << "` is not " << relation << " `" << superLeafAsString << "`";
|
||||
std::string baseReason = baseReasonBuilder.str();
|
||||
|
||||
std::stringstream reason;
|
||||
std::stringstream reason;
|
||||
|
||||
if (reasoning.subPath == reasoning.superPath)
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "` in the former type and `" << superLeafAsString
|
||||
<< "` in the latter type, and " << baseReason;
|
||||
else if (!reasoning.subPath.empty() && !reasoning.superPath.empty())
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "` and " << toStringHuman(reasoning.superPath) << "`"
|
||||
<< superLeafAsString << "`, and " << baseReason;
|
||||
else if (!reasoning.subPath.empty())
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "`, which is not " << relation << " `" << superLeafAsString
|
||||
<< "`";
|
||||
else
|
||||
reason << toStringHuman(reasoning.superPath) << "`" << superLeafAsString << "`, and " << baseReason;
|
||||
|
||||
reasons.push_back(reason.str());
|
||||
}
|
||||
if (reasoning.subPath == reasoning.superPath)
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "` in the former type and `" << superLeafAsString
|
||||
<< "` in the latter type, and " << baseReason;
|
||||
else if (!reasoning.subPath.empty() && !reasoning.superPath.empty())
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "` and " << toStringHuman(reasoning.superPath) << "`"
|
||||
<< superLeafAsString << "`, and " << baseReason;
|
||||
else if (!reasoning.subPath.empty())
|
||||
reason << toStringHuman(reasoning.subPath) << "`" << subLeafAsString << "`, which is not " << relation << " `" << superLeafAsString
|
||||
<< "`";
|
||||
else
|
||||
{
|
||||
std::string relation = "a subtype of";
|
||||
if (reasoning.variance == SubtypingVariance::Invariant)
|
||||
relation = "exactly";
|
||||
else if (reasoning.variance == SubtypingVariance::Contravariant)
|
||||
relation = "a supertype of";
|
||||
reason << toStringHuman(reasoning.superPath) << "`" << superLeafAsString << "`, and " << baseReason;
|
||||
|
||||
std::string reason;
|
||||
if (reasoning.subPath == reasoning.superPath)
|
||||
reason = "at " + toString(reasoning.subPath) + ", " + toString(subLeaf) + " is not " + relation + " " + toString(superLeaf);
|
||||
else
|
||||
reason = "type " + toString(subTy) + toString(reasoning.subPath, /* prefixDot */ true) + " (" + toString(subLeaf) + ") is not " +
|
||||
relation + " " + toString(superTy) + toString(reasoning.superPath, /* prefixDot */ true) + " (" + toString(superLeaf) + ")";
|
||||
|
||||
reasons.push_back(reason);
|
||||
}
|
||||
reasons.push_back(reason.str());
|
||||
|
||||
// if we haven't already proved this isn't suppressing, we have to keep checking.
|
||||
if (suppressed)
|
||||
|
@ -2850,6 +2862,12 @@ bool TypeChecker2::testIsSubtype(TypeId subTy, TypeId superTy, Location location
|
|||
NotNull<Scope> scope{findInnermostScope(location)};
|
||||
SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope);
|
||||
|
||||
if (FFlag::LuauReportSubtypingErrors)
|
||||
{
|
||||
if (!isErrorSuppressing(location, subTy))
|
||||
reportErrors(std::move(r.errors));
|
||||
}
|
||||
|
||||
if (r.normalizationTooComplex)
|
||||
reportError(NormalizationTooComplex{}, location);
|
||||
|
||||
|
@ -2864,6 +2882,12 @@ bool TypeChecker2::testIsSubtype(TypePackId subTy, TypePackId superTy, Location
|
|||
NotNull<Scope> scope{findInnermostScope(location)};
|
||||
SubtypingResult r = subtyping->isSubtype(subTy, superTy, scope);
|
||||
|
||||
if (FFlag::LuauReportSubtypingErrors)
|
||||
{
|
||||
if (!isErrorSuppressing(location, subTy))
|
||||
reportErrors(std::move(r.errors));
|
||||
}
|
||||
|
||||
if (r.normalizationTooComplex)
|
||||
reportError(NormalizationTooComplex{}, location);
|
||||
|
||||
|
|
|
@ -56,17 +56,16 @@ LUAU_FASTFLAGVARIABLE(LuauMetatableTypeFunctions)
|
|||
LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionImprovements)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIndexTypeFunctionFunctionMetamethods)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIntersectNotNil)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSkipNoRefineDuringRefinement)
|
||||
LUAU_FASTFLAGVARIABLE(LuauMetatablesHaveLength)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIndexAnyIsAny)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixCyclicIndexInIndexer)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSimplyRefineNotNil)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIndexDeferPendingIndexee)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNewTypeFunReductionChecks2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReduceUnionFollowUnionType)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRefineWaitForBlockedTypesInTarget)
|
||||
LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -2492,25 +2491,14 @@ struct CollectUnionTypeOptions : TypeOnceVisitor
|
|||
|
||||
bool visit(TypeId ty, const UnionType& ut) override
|
||||
{
|
||||
if (FFlag::LuauReduceUnionFollowUnionType)
|
||||
{
|
||||
// If we have something like:
|
||||
//
|
||||
// union<A | B, C | D>
|
||||
//
|
||||
// We probably just want to consider this to be the same as
|
||||
//
|
||||
// union<A, B, C, D>
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy of the default visit method.
|
||||
options.insert(ty);
|
||||
if (isPending(ty, ctx->solver))
|
||||
blockingTypes.insert(ty);
|
||||
return false;
|
||||
}
|
||||
// If we have something like:
|
||||
//
|
||||
// union<A | B, C | D>
|
||||
//
|
||||
// We probably just want to consider this to be the same as
|
||||
//
|
||||
// union<A, B, C, D>
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visit(TypeId ty, const TypeFunctionInstanceType& tfit) override
|
||||
|
@ -3600,8 +3588,8 @@ void BuiltinTypeFunctions::addToScope(NotNull<TypeArena> arena, NotNull<Scope> s
|
|||
return TypeFun{{genericT}, arena->addType(TypeFunctionInstanceType{NotNull{tf}, {t}, {}})};
|
||||
};
|
||||
|
||||
// make a type function for a two-argument type function
|
||||
auto mkBinaryTypeFunction = [&](const TypeFunction* tf)
|
||||
// make a type function for a two-argument type function with a default argument for the second type being the first
|
||||
auto mkBinaryTypeFunctionWithDefault = [&](const TypeFunction* tf)
|
||||
{
|
||||
TypeId t = arena->addType(GenericType{"T", Polarity::Negative});
|
||||
TypeId u = arena->addType(GenericType{"U", Polarity::Negative});
|
||||
|
@ -3611,31 +3599,53 @@ void BuiltinTypeFunctions::addToScope(NotNull<TypeArena> arena, NotNull<Scope> s
|
|||
return TypeFun{{genericT, genericU}, arena->addType(TypeFunctionInstanceType{NotNull{tf}, {t, u}, {}})};
|
||||
};
|
||||
|
||||
// make a two-argument type function without the default arguments
|
||||
auto mkBinaryTypeFunction = [&](const TypeFunction* tf)
|
||||
{
|
||||
TypeId t = arena->addType(GenericType{"T", Polarity::Negative});
|
||||
TypeId u = arena->addType(GenericType{"U", Polarity::Negative});
|
||||
GenericTypeDefinition genericT{t};
|
||||
GenericTypeDefinition genericU{u};
|
||||
|
||||
return TypeFun{{genericT, genericU}, arena->addType(TypeFunctionInstanceType{NotNull{tf}, {t, u}, {}})};
|
||||
};
|
||||
|
||||
scope->exportedTypeBindings[lenFunc.name] = mkUnaryTypeFunction(&lenFunc);
|
||||
scope->exportedTypeBindings[unmFunc.name] = mkUnaryTypeFunction(&unmFunc);
|
||||
|
||||
scope->exportedTypeBindings[addFunc.name] = mkBinaryTypeFunction(&addFunc);
|
||||
scope->exportedTypeBindings[subFunc.name] = mkBinaryTypeFunction(&subFunc);
|
||||
scope->exportedTypeBindings[mulFunc.name] = mkBinaryTypeFunction(&mulFunc);
|
||||
scope->exportedTypeBindings[divFunc.name] = mkBinaryTypeFunction(&divFunc);
|
||||
scope->exportedTypeBindings[idivFunc.name] = mkBinaryTypeFunction(&idivFunc);
|
||||
scope->exportedTypeBindings[powFunc.name] = mkBinaryTypeFunction(&powFunc);
|
||||
scope->exportedTypeBindings[modFunc.name] = mkBinaryTypeFunction(&modFunc);
|
||||
scope->exportedTypeBindings[concatFunc.name] = mkBinaryTypeFunction(&concatFunc);
|
||||
scope->exportedTypeBindings[addFunc.name] = mkBinaryTypeFunctionWithDefault(&addFunc);
|
||||
scope->exportedTypeBindings[subFunc.name] = mkBinaryTypeFunctionWithDefault(&subFunc);
|
||||
scope->exportedTypeBindings[mulFunc.name] = mkBinaryTypeFunctionWithDefault(&mulFunc);
|
||||
scope->exportedTypeBindings[divFunc.name] = mkBinaryTypeFunctionWithDefault(&divFunc);
|
||||
scope->exportedTypeBindings[idivFunc.name] = mkBinaryTypeFunctionWithDefault(&idivFunc);
|
||||
scope->exportedTypeBindings[powFunc.name] = mkBinaryTypeFunctionWithDefault(&powFunc);
|
||||
scope->exportedTypeBindings[modFunc.name] = mkBinaryTypeFunctionWithDefault(&modFunc);
|
||||
scope->exportedTypeBindings[concatFunc.name] = mkBinaryTypeFunctionWithDefault(&concatFunc);
|
||||
|
||||
scope->exportedTypeBindings[ltFunc.name] = mkBinaryTypeFunction(<Func);
|
||||
scope->exportedTypeBindings[leFunc.name] = mkBinaryTypeFunction(&leFunc);
|
||||
scope->exportedTypeBindings[eqFunc.name] = mkBinaryTypeFunction(&eqFunc);
|
||||
scope->exportedTypeBindings[ltFunc.name] = mkBinaryTypeFunctionWithDefault(<Func);
|
||||
scope->exportedTypeBindings[leFunc.name] = mkBinaryTypeFunctionWithDefault(&leFunc);
|
||||
scope->exportedTypeBindings[eqFunc.name] = mkBinaryTypeFunctionWithDefault(&eqFunc);
|
||||
|
||||
scope->exportedTypeBindings[keyofFunc.name] = mkUnaryTypeFunction(&keyofFunc);
|
||||
scope->exportedTypeBindings[rawkeyofFunc.name] = mkUnaryTypeFunction(&rawkeyofFunc);
|
||||
|
||||
scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunction(&indexFunc);
|
||||
scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunction(&rawgetFunc);
|
||||
if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults)
|
||||
{
|
||||
scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunction(&indexFunc);
|
||||
scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunction(&rawgetFunc);
|
||||
}
|
||||
else
|
||||
{
|
||||
scope->exportedTypeBindings[indexFunc.name] = mkBinaryTypeFunctionWithDefault(&indexFunc);
|
||||
scope->exportedTypeBindings[rawgetFunc.name] = mkBinaryTypeFunctionWithDefault(&rawgetFunc);
|
||||
}
|
||||
|
||||
if (FFlag::LuauMetatableTypeFunctions)
|
||||
{
|
||||
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunction(&setmetatableFunc);
|
||||
if (FFlag::LuauNotAllBinaryTypeFunsHaveDefaults)
|
||||
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunction(&setmetatableFunc);
|
||||
else
|
||||
scope->exportedTypeBindings[setmetatableFunc.name] = mkBinaryTypeFunctionWithDefault(&setmetatableFunc);
|
||||
scope->exportedTypeBindings[getmetatableFunc.name] = mkUnaryTypeFunction(&getmetatableFunc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
|||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStatForInFix)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLimitIterationWhenCheckingArgumentCounts)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -4120,15 +4119,12 @@ void TypeChecker::checkArgumentList(
|
|||
int loopCount = 0;
|
||||
auto exceedsLoopCount = [&]()
|
||||
{
|
||||
if (FFlag::LuauLimitIterationWhenCheckingArgumentCounts)
|
||||
++loopCount;
|
||||
if (loopCount > FInt::LuauTypeInferTypePackLoopLimit)
|
||||
{
|
||||
++loopCount;
|
||||
if (loopCount > FInt::LuauTypeInferTypePackLoopLimit)
|
||||
{
|
||||
state.reportError(TypeError{state.location, CodeTooComplex{}});
|
||||
reportErrorCodeTooComplex(state.location);
|
||||
return true;
|
||||
}
|
||||
state.reportError(TypeError{state.location, CodeTooComplex{}});
|
||||
reportErrorCodeTooComplex(state.location);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -330,6 +331,10 @@ bool Unifier2::unify(TypeId subTy, const FunctionType* superFn)
|
|||
{
|
||||
if (FFlag::LuauNonReentrantGeneralization2)
|
||||
{
|
||||
if (FFlag::DebugLuauGreedyGeneralization)
|
||||
genericPack = follow(genericPack);
|
||||
|
||||
// TODO: Clip this follow() with DebugLuauGreedyGeneralization
|
||||
const GenericTypePack* gen = get<GenericTypePack>(follow(genericPack));
|
||||
if (gen)
|
||||
genericPackSubstitutions[genericPack] = freshTypePack(scope, gen->polarity);
|
||||
|
|
|
@ -116,7 +116,9 @@ public:
|
|||
Position openGenericsPosition{0, 0};
|
||||
AstArray<Position> genericsCommaPositions;
|
||||
Position closeGenericsPosition{0, 0};
|
||||
AstArray<Position> argsAnnotationColonPositions;
|
||||
AstArray<Position> argsCommaPositions;
|
||||
Position varargAnnotationColonPosition{0, 0};
|
||||
Position returnSpecifierPosition{0, 0};
|
||||
};
|
||||
|
||||
|
@ -224,8 +226,9 @@ class CstStatLocal : public CstNode
|
|||
public:
|
||||
LUAU_CST_RTTI(CstStatLocal)
|
||||
|
||||
CstStatLocal(AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions);
|
||||
CstStatLocal(AstArray<Position> varsAnnotationColonPositions, AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions);
|
||||
|
||||
AstArray<Position> varsAnnotationColonPositions;
|
||||
AstArray<Position> varsCommaPositions;
|
||||
AstArray<Position> valuesCommaPositions;
|
||||
};
|
||||
|
@ -235,8 +238,9 @@ class CstStatFor : public CstNode
|
|||
public:
|
||||
LUAU_CST_RTTI(CstStatFor)
|
||||
|
||||
CstStatFor(Position equalsPosition, Position endCommaPosition, std::optional<Position> stepCommaPosition);
|
||||
CstStatFor(Position annotationColonPosition, Position equalsPosition, Position endCommaPosition, std::optional<Position> stepCommaPosition);
|
||||
|
||||
Position annotationColonPosition;
|
||||
Position equalsPosition;
|
||||
Position endCommaPosition;
|
||||
std::optional<Position> stepCommaPosition;
|
||||
|
@ -247,8 +251,9 @@ class CstStatForIn : public CstNode
|
|||
public:
|
||||
LUAU_CST_RTTI(CstStatForIn)
|
||||
|
||||
CstStatForIn(AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions);
|
||||
CstStatForIn(AstArray<Position> varsAnnotationColonPositions, AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions);
|
||||
|
||||
AstArray<Position> varsAnnotationColonPositions;
|
||||
AstArray<Position> varsCommaPositions;
|
||||
AstArray<Position> valuesCommaPositions;
|
||||
};
|
||||
|
|
|
@ -196,6 +196,7 @@ private:
|
|||
|
||||
// binding ::= Name [`:` Type]
|
||||
Binding parseBinding();
|
||||
AstArray<Position> extractAnnotationColonPositions(const TempVector<Binding>& bindings);
|
||||
|
||||
// bindinglist ::= (binding | `...') {`,' bindinglist}
|
||||
// Returns the location of the vararg ..., or std::nullopt if the function is not vararg.
|
||||
|
@ -203,7 +204,8 @@ private:
|
|||
TempVector<Binding>& result,
|
||||
bool allowDot3 = false,
|
||||
AstArray<Position>* commaPositions = nullptr,
|
||||
std::optional<Position> initialCommaPosition = std::nullopt
|
||||
Position* initialCommaPosition = nullptr,
|
||||
Position* varargAnnotationColonPosition = nullptr
|
||||
);
|
||||
|
||||
AstType* parseOptionalType();
|
||||
|
@ -452,10 +454,12 @@ private:
|
|||
{
|
||||
Name name;
|
||||
AstType* annotation;
|
||||
Position colonPosition;
|
||||
|
||||
explicit Binding(const Name& name, AstType* annotation = nullptr)
|
||||
explicit Binding(const Name& name, AstType* annotation = nullptr, Position colonPosition = {0,0})
|
||||
: name(name)
|
||||
, annotation(annotation)
|
||||
, colonPosition(colonPosition)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "Luau/Common.h"
|
||||
|
||||
LUAU_FASTFLAG(LuauDeprecatedAttribute);
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
namespace Luau
|
||||
|
@ -11,8 +10,6 @@ namespace Luau
|
|||
|
||||
static bool hasAttributeInArray(const AstArray<AstAttr*> attributes, AstAttr::Type attributeType)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
for (const auto attribute : attributes)
|
||||
{
|
||||
if (attribute->type == attributeType)
|
||||
|
@ -338,8 +335,6 @@ bool AstExprFunction::hasNativeAttribute() const
|
|||
|
||||
bool AstExprFunction::hasAttribute(const AstAttr::Type attributeType) const
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
return hasAttributeInArray(attributes, attributeType);
|
||||
}
|
||||
|
||||
|
@ -1026,8 +1021,6 @@ bool AstStatDeclareFunction::isCheckedFunction() const
|
|||
|
||||
bool AstStatDeclareFunction::hasAttribute(AstAttr::Type attributeType) const
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
return hasAttributeInArray(attributes, attributeType);
|
||||
}
|
||||
|
||||
|
@ -1244,8 +1237,6 @@ bool AstTypeFunction::isCheckedFunction() const
|
|||
|
||||
bool AstTypeFunction::hasAttribute(AstAttr::Type attributeType) const
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
return hasAttributeInArray(attributes, attributeType);
|
||||
}
|
||||
|
||||
|
|
|
@ -94,23 +94,26 @@ CstStatReturn::CstStatReturn(AstArray<Position> commaPositions)
|
|||
{
|
||||
}
|
||||
|
||||
CstStatLocal::CstStatLocal(AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions)
|
||||
CstStatLocal::CstStatLocal(AstArray<Position> varsAnnotationColonPositions, AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions)
|
||||
: CstNode(CstClassIndex())
|
||||
, varsAnnotationColonPositions(varsAnnotationColonPositions)
|
||||
, varsCommaPositions(varsCommaPositions)
|
||||
, valuesCommaPositions(valuesCommaPositions)
|
||||
{
|
||||
}
|
||||
|
||||
CstStatFor::CstStatFor(Position equalsPosition, Position endCommaPosition, std::optional<Position> stepCommaPosition)
|
||||
CstStatFor::CstStatFor(Position annotationColonPosition, Position equalsPosition, Position endCommaPosition, std::optional<Position> stepCommaPosition)
|
||||
: CstNode(CstClassIndex())
|
||||
, annotationColonPosition(annotationColonPosition)
|
||||
, equalsPosition(equalsPosition)
|
||||
, endCommaPosition(endCommaPosition)
|
||||
, stepCommaPosition(stepCommaPosition)
|
||||
{
|
||||
}
|
||||
|
||||
CstStatForIn::CstStatForIn(AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions)
|
||||
CstStatForIn::CstStatForIn(AstArray<Position> varsAnnotationColonPositions, AstArray<Position> varsCommaPositions, AstArray<Position> valuesCommaPositions)
|
||||
: CstNode(CstClassIndex())
|
||||
, varsAnnotationColonPositions(varsAnnotationColonPositions)
|
||||
, varsCommaPositions(varsCommaPositions)
|
||||
, valuesCommaPositions(valuesCommaPositions)
|
||||
{
|
||||
|
|
|
@ -19,14 +19,12 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
|||
// See docs/SyntaxChanges.md for an explanation.
|
||||
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseOptionalAsNode2)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDeclareExternType)
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypeFunResultInAutocomplete)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDeprecatedAttribute)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixFunctionWithAttributesStartLocation)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStoreLocalAnnotationColonPositions)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
|
||||
|
||||
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
|
||||
|
@ -644,7 +642,8 @@ AstStat* Parser::parseFor()
|
|||
{
|
||||
AstStatFor* node = allocator.alloc<AstStatFor>(Location(start, end), var, from, to, step, body, hasDo, matchDo.location);
|
||||
if (options.storeCstData)
|
||||
cstNodeMap[node] = allocator.alloc<CstStatFor>(equalsPosition, endCommaPosition, stepCommaPosition);
|
||||
cstNodeMap[node] = allocator.alloc<CstStatFor>(varname.colonPosition, equalsPosition, endCommaPosition, stepCommaPosition);
|
||||
|
||||
return node;
|
||||
}
|
||||
else
|
||||
|
@ -664,7 +663,7 @@ AstStat* Parser::parseFor()
|
|||
{
|
||||
Position initialCommaPosition = lexer.current().location.begin;
|
||||
nextLexeme();
|
||||
parseBindingList(names, false, &varsCommaPosition, initialCommaPosition);
|
||||
parseBindingList(names, false, &varsCommaPosition, &initialCommaPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -709,7 +708,12 @@ AstStat* Parser::parseFor()
|
|||
AstStatForIn* node =
|
||||
allocator.alloc<AstStatForIn>(Location(start, end), copy(vars), copy(values), body, hasIn, inLocation, hasDo, matchDo.location);
|
||||
if (options.storeCstData)
|
||||
cstNodeMap[node] = allocator.alloc<CstStatForIn>(varsCommaPosition, copy(valuesCommaPositions));
|
||||
{
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
cstNodeMap[node] = allocator.alloc<CstStatForIn>(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions));
|
||||
else
|
||||
cstNodeMap[node] = allocator.alloc<CstStatForIn>(AstArray<Position>{}, varsCommaPosition, copy(valuesCommaPositions));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
else
|
||||
|
@ -825,7 +829,7 @@ std::pair<bool, AstAttr::Type> Parser::validateAttribute(const char* attributeNa
|
|||
}
|
||||
}
|
||||
|
||||
if (!found || (!FFlag::LuauDeprecatedAttribute && type == AstAttr::Type::Deprecated))
|
||||
if (!found)
|
||||
{
|
||||
if (strlen(attributeName) == 1)
|
||||
report(lexer.current().location, "Attribute name is missing");
|
||||
|
@ -1010,7 +1014,13 @@ AstStat* Parser::parseLocal(const AstArray<AstAttr*>& attributes)
|
|||
{
|
||||
AstStatLocal* node = allocator.alloc<AstStatLocal>(Location(start, end), copy(vars), copy(values), equalsSignLocation);
|
||||
if (options.storeCstData)
|
||||
cstNodeMap[node] = allocator.alloc<CstStatLocal>(varsCommaPositions, copy(valuesCommaPositions));
|
||||
{
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
cstNodeMap[node] = allocator.alloc<CstStatLocal>(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions));
|
||||
else
|
||||
cstNodeMap[node] = allocator.alloc<CstStatLocal>(AstArray<Position>{}, varsCommaPositions, copy(valuesCommaPositions));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
else
|
||||
|
@ -1140,8 +1150,6 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Positio
|
|||
|
||||
AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArray<AstAttr*>& attributes)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauDeprecatedAttribute);
|
||||
|
||||
Location start = lexer.current().location;
|
||||
|
||||
nextLexeme();
|
||||
|
@ -1225,85 +1233,6 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArr
|
|||
return AstDeclaredExternTypeProperty{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||
}
|
||||
|
||||
AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod_DEPRECATED()
|
||||
{
|
||||
Location start = lexer.current().location;
|
||||
|
||||
nextLexeme();
|
||||
|
||||
Name fnName = parseName("function name");
|
||||
|
||||
// TODO: generic method declarations CLI-39909
|
||||
AstArray<AstGenericType*> generics;
|
||||
AstArray<AstGenericTypePack*> genericPacks;
|
||||
generics.size = 0;
|
||||
generics.data = nullptr;
|
||||
genericPacks.size = 0;
|
||||
genericPacks.data = nullptr;
|
||||
|
||||
MatchLexeme matchParen = lexer.current();
|
||||
expectAndConsume('(', "function parameter list start");
|
||||
|
||||
TempVector<Binding> args(scratchBinding);
|
||||
|
||||
bool vararg = false;
|
||||
Location varargLocation;
|
||||
AstTypePack* varargAnnotation = nullptr;
|
||||
if (lexer.current().type != ')')
|
||||
std::tie(vararg, varargLocation, varargAnnotation) = parseBindingList(args, /* allowDot3 */ true);
|
||||
|
||||
expectMatchAndConsume(')', matchParen);
|
||||
|
||||
AstTypePack* retTypes;
|
||||
AstTypeList retTypes_DEPRECATED;
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
retTypes = parseOptionalReturnType();
|
||||
if (!retTypes)
|
||||
retTypes = allocator.alloc<AstTypePackExplicit>(lexer.current().location, AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||
}
|
||||
else
|
||||
{
|
||||
retTypes_DEPRECATED = parseOptionalReturnType_DEPRECATED().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
|
||||
}
|
||||
Location end = lexer.previousLocation();
|
||||
|
||||
TempVector<AstType*> vars(scratchType);
|
||||
TempVector<std::optional<AstArgumentName>> varNames(scratchOptArgName);
|
||||
|
||||
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
||||
{
|
||||
return AstDeclaredExternTypeProperty{
|
||||
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
|
||||
};
|
||||
}
|
||||
|
||||
// Skip the first index.
|
||||
for (size_t i = 1; i < args.size(); ++i)
|
||||
{
|
||||
varNames.push_back(AstArgumentName{args[i].name.name, args[i].name.location});
|
||||
|
||||
if (args[i].annotation)
|
||||
vars.push_back(args[i].annotation);
|
||||
else
|
||||
vars.push_back(reportTypeError(Location(start, end), {}, "All declaration parameters aside from 'self' must be annotated"));
|
||||
}
|
||||
|
||||
if (vararg && !varargAnnotation)
|
||||
report(start, "All declaration parameters aside from 'self' must be annotated");
|
||||
|
||||
AstType* fnType =
|
||||
FFlag::LuauStoreReturnTypesAsPackOnAst
|
||||
? allocator.alloc<AstTypeFunction>(
|
||||
Location(start, end), generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes
|
||||
)
|
||||
: allocator.alloc<AstTypeFunction>(
|
||||
Location(start, end), generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes_DEPRECATED
|
||||
);
|
||||
|
||||
return AstDeclaredExternTypeProperty{fnName.name, fnName.location, fnType, true, Location(start, end)};
|
||||
}
|
||||
|
||||
AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*>& attributes)
|
||||
{
|
||||
// `declare` token is already parsed at this point
|
||||
|
@ -1445,7 +1374,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
{
|
||||
AstArray<AstAttr*> attributes{nullptr, 0};
|
||||
|
||||
if (FFlag::LuauDeprecatedAttribute && (lexer.current().type == Lexeme::Attribute))
|
||||
if (lexer.current().type == Lexeme::Attribute)
|
||||
{
|
||||
attributes = Parser::parseAttributes();
|
||||
|
||||
|
@ -1464,10 +1393,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
// There are two possibilities: Either it's a property or a function.
|
||||
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
props.push_back(parseDeclaredExternTypeMethod(attributes));
|
||||
else
|
||||
props.push_back(parseDeclaredExternTypeMethod_DEPRECATED());
|
||||
props.push_back(parseDeclaredExternTypeMethod(attributes));
|
||||
}
|
||||
else if (lexer.current().type == '[')
|
||||
{
|
||||
|
@ -1543,10 +1469,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
|
|||
// There are two possibilities: Either it's a property or a function.
|
||||
if (lexer.current().type == Lexeme::ReservedFunction)
|
||||
{
|
||||
if (FFlag::LuauDeprecatedAttribute)
|
||||
props.push_back(parseDeclaredExternTypeMethod(attributes));
|
||||
else
|
||||
props.push_back(parseDeclaredExternTypeMethod_DEPRECATED());
|
||||
props.push_back(parseDeclaredExternTypeMethod(attributes));
|
||||
}
|
||||
else if (lexer.current().type == '[' &&
|
||||
(lexer.lookahead().type == Lexeme::RawString || lexer.lookahead().type == Lexeme::QuotedString))
|
||||
|
@ -1786,8 +1709,19 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
|||
AstTypePack* varargAnnotation = nullptr;
|
||||
|
||||
if (lexer.current().type != ')')
|
||||
std::tie(vararg, varargLocation, varargAnnotation) =
|
||||
parseBindingList(args, /* allowDot3= */ true, cstNode ? &cstNode->argsCommaPositions : nullptr);
|
||||
{
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
{
|
||||
if (cstNode)
|
||||
std::tie(vararg, varargLocation, varargAnnotation) =
|
||||
parseBindingList(args, /* allowDot3= */ true, &cstNode->argsCommaPositions, nullptr, &cstNode->varargAnnotationColonPosition);
|
||||
else
|
||||
std::tie(vararg, varargLocation, varargAnnotation) = parseBindingList(args, /* allowDot3= */ true);
|
||||
}
|
||||
else
|
||||
std::tie(vararg, varargLocation, varargAnnotation) =
|
||||
parseBindingList(args, /* allowDot3= */ true, cstNode ? &cstNode->argsCommaPositions : nullptr);
|
||||
}
|
||||
|
||||
std::optional<Location> argLocation;
|
||||
|
||||
|
@ -1846,6 +1780,8 @@ std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
|
|||
if (options.storeCstData)
|
||||
{
|
||||
cstNode->functionKeywordPosition = matchFunction.location.begin;
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions)
|
||||
cstNode->argsAnnotationColonPositions = extractAnnotationColonPositions(args);
|
||||
cstNodeMap[node] = cstNode;
|
||||
}
|
||||
|
||||
|
@ -2042,9 +1978,22 @@ Parser::Binding Parser::parseBinding()
|
|||
if (!name)
|
||||
name = Name(nameError, lexer.current().location);
|
||||
|
||||
Position colonPosition = lexer.current().location.begin;
|
||||
AstType* annotation = parseOptionalType();
|
||||
|
||||
return Binding(*name, annotation);
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions && options.storeCstData)
|
||||
return Binding(*name, annotation, colonPosition);
|
||||
else
|
||||
return Binding(*name, annotation);
|
||||
}
|
||||
|
||||
AstArray<Position> Parser::extractAnnotationColonPositions(const TempVector<Binding>& bindings)
|
||||
{
|
||||
LUAU_ASSERT(FFlag::LuauStoreLocalAnnotationColonPositions);
|
||||
TempVector<Position> annotationColonPositions(scratchPosition);
|
||||
for (size_t i = 0; i < bindings.size(); ++i)
|
||||
annotationColonPositions.push_back(bindings[i].colonPosition);
|
||||
return copy(annotationColonPositions);
|
||||
}
|
||||
|
||||
// bindinglist ::= (binding | `...') [`,' bindinglist]
|
||||
|
@ -2052,7 +2001,8 @@ std::tuple<bool, Location, AstTypePack*> Parser::parseBindingList(
|
|||
TempVector<Binding>& result,
|
||||
bool allowDot3,
|
||||
AstArray<Position>* commaPositions,
|
||||
std::optional<Position> initialCommaPosition
|
||||
Position* initialCommaPosition,
|
||||
Position* varargAnnotationColonPosition
|
||||
)
|
||||
{
|
||||
TempVector<Position> localCommaPositions(scratchPosition);
|
||||
|
@ -2070,6 +2020,9 @@ std::tuple<bool, Location, AstTypePack*> Parser::parseBindingList(
|
|||
AstTypePack* tailAnnotation = nullptr;
|
||||
if (lexer.current().type == ':')
|
||||
{
|
||||
if (FFlag::LuauStoreLocalAnnotationColonPositions && varargAnnotationColonPosition)
|
||||
*varargAnnotationColonPosition = lexer.current().location.begin;
|
||||
|
||||
nextLexeme();
|
||||
tailAnnotation = parseVariadicArgumentTypePack();
|
||||
}
|
||||
|
@ -2256,8 +2209,6 @@ AstTypePack* Parser::parseReturnType()
|
|||
|
||||
nextLexeme();
|
||||
|
||||
Location innerBegin = lexer.current().location;
|
||||
|
||||
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
|
||||
|
||||
TempVector<AstType*> result(scratchType);
|
||||
|
@ -2283,47 +2234,25 @@ AstTypePack* Parser::parseReturnType()
|
|||
if (lexer.current().type != Lexeme::SkinnyArrow && resultNames.empty())
|
||||
{
|
||||
// If it turns out that it's just '(A)', it's possible that there are unions/intersections to follow, so fold over it.
|
||||
if (FFlag::LuauAstTypeGroup3)
|
||||
if (result.size() == 1)
|
||||
{
|
||||
if (result.size() == 1)
|
||||
{
|
||||
// TODO(CLI-140667): stop parsing type suffix when varargAnnotation != nullptr - this should be a parse error
|
||||
AstType* inner = varargAnnotation == nullptr ? allocator.alloc<AstTypeGroup>(location, result[0]) : result[0];
|
||||
AstType* returnType = parseTypeSuffix(inner, begin.location);
|
||||
// TODO(CLI-140667): stop parsing type suffix when varargAnnotation != nullptr - this should be a parse error
|
||||
AstType* inner = varargAnnotation == nullptr ? allocator.alloc<AstTypeGroup>(location, result[0]) : result[0];
|
||||
AstType* returnType = parseTypeSuffix(inner, begin.location);
|
||||
|
||||
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
|
||||
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
|
||||
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
|
||||
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
|
||||
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
|
||||
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
|
||||
|
||||
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
|
||||
// type to successfully parse. We need the span of the whole annotation.
|
||||
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
|
||||
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
|
||||
// type to successfully parse. We need the span of the whole annotation.
|
||||
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
|
||||
|
||||
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation});
|
||||
if (options.storeCstData)
|
||||
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
|
||||
return node;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.size() == 1)
|
||||
{
|
||||
AstType* returnType = parseTypeSuffix(result[0], innerBegin);
|
||||
|
||||
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
|
||||
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
|
||||
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
|
||||
|
||||
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
|
||||
// type to successfully parse. We need the span of the whole annotation.
|
||||
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
|
||||
|
||||
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation});
|
||||
if (options.storeCstData)
|
||||
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
|
||||
return node;
|
||||
}
|
||||
AstTypePackExplicit* node =
|
||||
allocator.alloc<AstTypePackExplicit>(Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation});
|
||||
if (options.storeCstData)
|
||||
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
|
||||
return node;
|
||||
}
|
||||
|
||||
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(location, AstTypeList{copy(result), varargAnnotation});
|
||||
|
@ -2366,8 +2295,6 @@ std::pair<Location, AstTypeList> Parser::parseReturnType_DEPRECATED()
|
|||
|
||||
nextLexeme();
|
||||
|
||||
Location innerBegin = lexer.current().location;
|
||||
|
||||
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
|
||||
|
||||
TempVector<AstType*> result(scratchType);
|
||||
|
@ -2387,42 +2314,23 @@ std::pair<Location, AstTypeList> Parser::parseReturnType_DEPRECATED()
|
|||
if (lexer.current().type != Lexeme::SkinnyArrow && resultNames.empty())
|
||||
{
|
||||
// If it turns out that it's just '(A)', it's possible that there are unions/intersections to follow, so fold over it.
|
||||
if (FFlag::LuauAstTypeGroup3)
|
||||
if (result.size() == 1)
|
||||
{
|
||||
if (result.size() == 1)
|
||||
{
|
||||
// TODO(CLI-140667): stop parsing type suffix when varargAnnotation != nullptr - this should be a parse error
|
||||
AstType* inner = varargAnnotation == nullptr ? allocator.alloc<AstTypeGroup>(location, result[0]) : result[0];
|
||||
AstType* returnType = parseTypeSuffix(inner, begin.location);
|
||||
// TODO(CLI-140667): stop parsing type suffix when varargAnnotation != nullptr - this should be a parse error
|
||||
AstType* inner = varargAnnotation == nullptr ? allocator.alloc<AstTypeGroup>(location, result[0]) : result[0];
|
||||
AstType* returnType = parseTypeSuffix(inner, begin.location);
|
||||
|
||||
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
|
||||
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
|
||||
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
|
||||
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
|
||||
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
|
||||
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
|
||||
|
||||
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
|
||||
// type to successfully parse. We need the span of the whole annotation.
|
||||
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
|
||||
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
|
||||
// type to successfully parse. We need the span of the whole annotation.
|
||||
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
|
||||
|
||||
return {Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation}};
|
||||
}
|
||||
return {Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation}};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.size() == 1)
|
||||
{
|
||||
AstType* returnType = parseTypeSuffix(result[0], innerBegin);
|
||||
|
||||
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
|
||||
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
|
||||
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
|
||||
|
||||
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
|
||||
// type to successfully parse. We need the span of the whole annotation.
|
||||
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
|
||||
|
||||
return {Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation}};
|
||||
}
|
||||
}
|
||||
|
||||
return {location, AstTypeList{copy(result), varargAnnotation}};
|
||||
}
|
||||
|
@ -2888,10 +2796,7 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray<AstAttr*>
|
|||
}
|
||||
else
|
||||
{
|
||||
if (FFlag::LuauAstTypeGroup3)
|
||||
return {allocator.alloc<AstTypeGroup>(Location(parameterStart.location, closeArgsLocation), params[0]), {}};
|
||||
else
|
||||
return {params[0], {}};
|
||||
return {allocator.alloc<AstTypeGroup>(Location(parameterStart.location, closeArgsLocation), params[0]), {}};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3012,8 +2917,6 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin)
|
|||
|
||||
bool isUnion = false;
|
||||
bool isIntersection = false;
|
||||
// Clip with FFlag::LuauParseOptionalAsNode2
|
||||
bool hasOptional_DEPRECATED = false;
|
||||
unsigned int optionalCount = 0;
|
||||
|
||||
Location location = begin;
|
||||
|
@ -3047,19 +2950,10 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin)
|
|||
Location loc = lexer.current().location;
|
||||
nextLexeme();
|
||||
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
parts.push_back(allocator.alloc<AstTypeOptional>(Location(loc)));
|
||||
optionalCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hasOptional_DEPRECATED)
|
||||
parts.push_back(allocator.alloc<AstTypeReference>(loc, std::nullopt, nameNil, std::nullopt, loc));
|
||||
}
|
||||
parts.push_back(allocator.alloc<AstTypeOptional>(Location(loc)));
|
||||
optionalCount++;
|
||||
|
||||
isUnion = true;
|
||||
hasOptional_DEPRECATED = true;
|
||||
}
|
||||
else if (c == '&')
|
||||
{
|
||||
|
@ -3087,16 +2981,8 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin)
|
|||
else
|
||||
break;
|
||||
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
{
|
||||
if (parts.size() > unsigned(FInt::LuauTypeLengthLimit) + optionalCount)
|
||||
ParseError::raise(parts.back()->location, "Exceeded allowed type length; simplify your type annotation to make the code compile");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parts.size() > unsigned(FInt::LuauTypeLengthLimit) + hasOptional_DEPRECATED)
|
||||
ParseError::raise(parts.back()->location, "Exceeded allowed type length; simplify your type annotation to make the code compile");
|
||||
}
|
||||
if (parts.size() > unsigned(FInt::LuauTypeLengthLimit) + optionalCount)
|
||||
ParseError::raise(parts.back()->location, "Exceeded allowed type length; simplify your type annotation to make the code compile");
|
||||
}
|
||||
|
||||
if (parts.size() == 1 && !isUnion && !isIntersection)
|
||||
|
@ -4520,15 +4406,10 @@ AstArray<AstTypeOrPack> Parser::parseTypeParams(Position* openingPosition, TempV
|
|||
// the next lexeme is one that follows a type
|
||||
// (&, |, ?), then assume that this was actually a
|
||||
// parenthesized type.
|
||||
if (FFlag::LuauAstTypeGroup3)
|
||||
{
|
||||
auto parenthesizedType = explicitTypePack->typeList.types.data[0];
|
||||
parameters.push_back(
|
||||
{parseTypeSuffix(allocator.alloc<AstTypeGroup>(parenthesizedType->location, parenthesizedType), begin), {}}
|
||||
);
|
||||
}
|
||||
else
|
||||
parameters.push_back({parseTypeSuffix(explicitTypePack->typeList.types.data[0], begin), {}});
|
||||
auto parenthesizedType = explicitTypePack->typeList.types.data[0];
|
||||
parameters.push_back(
|
||||
{parseTypeSuffix(allocator.alloc<AstTypeGroup>(parenthesizedType->location, parenthesizedType), begin), {}}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@ using AddCompletionCallback = std::function<void(const std::string& completion,
|
|||
|
||||
// Note: These are internal functions which are being exposed in a header
|
||||
// so they can be included by unit tests.
|
||||
void* createCliRequireContext(lua_State* L);
|
||||
void setupState(lua_State* L);
|
||||
std::string runCode(lua_State* L, const std::string& source);
|
||||
void getCompletions(lua_State* L, const std::string& editBuffer, const AddCompletionCallback& addCompletionCallback);
|
||||
|
|
|
@ -7,24 +7,27 @@
|
|||
|
||||
#include "lua.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
void requireConfigInit(luarequire_Configuration* config);
|
||||
|
||||
struct ReplRequirer
|
||||
{
|
||||
using CompileOptions = Luau::CompileOptions(*)();
|
||||
using BoolCheck = bool(*)();
|
||||
using Coverage = void(*)(lua_State*, int);
|
||||
|
||||
ReplRequirer(
|
||||
std::function<Luau::CompileOptions()> copts,
|
||||
std::function<bool()> coverageActive,
|
||||
std::function<bool()> codegenEnabled,
|
||||
std::function<void(lua_State*, int)> coverageTrack
|
||||
CompileOptions copts,
|
||||
BoolCheck coverageActive,
|
||||
BoolCheck codegenEnabled,
|
||||
Coverage coverageTrack
|
||||
);
|
||||
|
||||
std::function<Luau::CompileOptions()> copts;
|
||||
std::function<bool()> coverageActive;
|
||||
std::function<bool()> codegenEnabled;
|
||||
std::function<void(lua_State*, int)> coverageTrack;
|
||||
CompileOptions copts;
|
||||
BoolCheck coverageActive;
|
||||
BoolCheck codegenEnabled;
|
||||
Coverage coverageTrack;
|
||||
|
||||
std::string absPath;
|
||||
std::string relPath;
|
||||
|
|
|
@ -164,7 +164,7 @@ static int lua_callgrind(lua_State* L)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void* createCliRequireContext(lua_State* L)
|
||||
void* createCliRequireContext(lua_State* L)
|
||||
{
|
||||
void* ctx = lua_newuserdatadtor(
|
||||
L,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Luau/CodeGen.h"
|
||||
#include "Luau/CodeGenOptions.h"
|
||||
#include "Luau/FileUtils.h"
|
||||
#include "Luau/Require.h"
|
||||
|
||||
#include "Luau/RequirerUtils.h"
|
||||
|
@ -101,18 +102,18 @@ static bool is_module_present(lua_State* L, void* ctx)
|
|||
return isFilePresent(req->absPath, req->suffix);
|
||||
}
|
||||
|
||||
static luarequire_WriteResult get_contents(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
||||
{
|
||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||
return write(getFileContents(req->absPath, req->suffix), buffer, buffer_size, size_out);
|
||||
}
|
||||
|
||||
static luarequire_WriteResult get_chunkname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
||||
{
|
||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||
return write("@" + req->relPath, buffer, buffer_size, size_out);
|
||||
}
|
||||
|
||||
static luarequire_WriteResult get_loadname(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
||||
{
|
||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||
return write(req->absPath + req->suffix, buffer, buffer_size, size_out);
|
||||
}
|
||||
|
||||
static luarequire_WriteResult get_cache_key(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out)
|
||||
{
|
||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||
|
@ -131,7 +132,7 @@ static luarequire_WriteResult get_config(lua_State* L, void* ctx, char* buffer,
|
|||
return write(getFileContents(req->absPath, "/.luaurc"), buffer, buffer_size, size_out);
|
||||
}
|
||||
|
||||
static int load(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* contents)
|
||||
static int load(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* loadname)
|
||||
{
|
||||
ReplRequirer* req = static_cast<ReplRequirer*>(ctx);
|
||||
|
||||
|
@ -144,8 +145,12 @@ static int load(lua_State* L, void* ctx, const char* path, const char* chunkname
|
|||
// new thread needs to have the globals sandboxed
|
||||
luaL_sandboxthread(ML);
|
||||
|
||||
std::optional<std::string> contents = readFile(loadname);
|
||||
if (!contents)
|
||||
luaL_error(L, "could not read file '%s'", loadname);
|
||||
|
||||
// now we can compile & run module on the new thread
|
||||
std::string bytecode = Luau::compile(contents, req->copts());
|
||||
std::string bytecode = Luau::compile(*contents, req->copts());
|
||||
if (luau_load(ML, chunkname, bytecode.data(), bytecode.size(), 0) == 0)
|
||||
{
|
||||
if (req->codegenEnabled())
|
||||
|
@ -199,23 +204,18 @@ void requireConfigInit(luarequire_Configuration* config)
|
|||
config->to_parent = to_parent;
|
||||
config->to_child = to_child;
|
||||
config->is_module_present = is_module_present;
|
||||
config->get_contents = get_contents;
|
||||
config->is_config_present = is_config_present;
|
||||
config->get_chunkname = get_chunkname;
|
||||
config->get_loadname = get_loadname;
|
||||
config->get_cache_key = get_cache_key;
|
||||
config->get_config = get_config;
|
||||
config->load = load;
|
||||
}
|
||||
|
||||
ReplRequirer::ReplRequirer(
|
||||
std::function<Luau::CompileOptions()> copts,
|
||||
std::function<bool()> coverageActive,
|
||||
std::function<bool()> codegenEnabled,
|
||||
std::function<void(lua_State*, int)> coverageTrack
|
||||
)
|
||||
: copts(std::move(copts))
|
||||
, coverageActive(std::move(coverageActive))
|
||||
, codegenEnabled(std::move(codegenEnabled))
|
||||
, coverageTrack(std::move(coverageTrack))
|
||||
ReplRequirer::ReplRequirer(CompileOptions copts, BoolCheck coverageActive, BoolCheck codegenEnabled, Coverage coverageTrack)
|
||||
: copts(copts)
|
||||
, coverageActive(coverageActive)
|
||||
, codegenEnabled(codegenEnabled)
|
||||
, coverageTrack(coverageTrack)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -83,15 +83,15 @@ struct luarequire_Configuration
|
|||
// Returns whether the context is currently pointing at a module.
|
||||
bool (*is_module_present)(lua_State* L, void* ctx);
|
||||
|
||||
// Provides the contents of the current module. This function is only called
|
||||
// if is_module_present returns true.
|
||||
luarequire_WriteResult (*get_contents)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
||||
|
||||
// Provides a chunkname for the current module. This will be accessible
|
||||
// through the debug library. This function is only called if
|
||||
// is_module_present returns true.
|
||||
luarequire_WriteResult (*get_chunkname)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
||||
|
||||
// Provides a loadname that identifies the current module and is passed to
|
||||
// load. This function is only called if is_module_present returns true.
|
||||
luarequire_WriteResult (*get_loadname)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
||||
|
||||
// Provides a cache key representing the current module. This function is
|
||||
// only called if is_module_present returns true.
|
||||
luarequire_WriteResult (*get_cache_key)(lua_State* L, void* ctx, char* buffer, size_t buffer_size, size_t* size_out);
|
||||
|
@ -109,7 +109,7 @@ struct luarequire_Configuration
|
|||
// number of results placed on the stack. Returning -1 directs the requiring
|
||||
// thread to yield. In this case, this thread should be resumed with the
|
||||
// module result pushed onto its stack.
|
||||
int (*load)(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* contents);
|
||||
int (*load)(lua_State* L, void* ctx, const char* path, const char* chunkname, const char* loadname);
|
||||
};
|
||||
|
||||
// Populates function pointers in the given luarequire_Configuration.
|
||||
|
|
|
@ -62,16 +62,16 @@ bool RuntimeNavigationContext::isModulePresent() const
|
|||
return config->is_module_present(L, ctx);
|
||||
}
|
||||
|
||||
std::optional<std::string> RuntimeNavigationContext::getContents() const
|
||||
{
|
||||
return getStringFromCWriter(config->get_contents, initalFileBufferSize);
|
||||
}
|
||||
|
||||
std::optional<std::string> RuntimeNavigationContext::getChunkname() const
|
||||
{
|
||||
return getStringFromCWriter(config->get_chunkname, initalIdentifierBufferSize);
|
||||
}
|
||||
|
||||
std::optional<std::string> RuntimeNavigationContext::getLoadname() const
|
||||
{
|
||||
return getStringFromCWriter(config->get_loadname, initalIdentifierBufferSize);
|
||||
}
|
||||
|
||||
std::optional<std::string> RuntimeNavigationContext::getCacheKey() const
|
||||
{
|
||||
return getStringFromCWriter(config->get_cache_key, initalIdentifierBufferSize);
|
||||
|
|
|
@ -31,8 +31,8 @@ public:
|
|||
|
||||
// Custom capabilities
|
||||
bool isModulePresent() const;
|
||||
std::optional<std::string> getContents() const;
|
||||
std::optional<std::string> getChunkname() const;
|
||||
std::optional<std::string> getLoadname() const;
|
||||
std::optional<std::string> getCacheKey() const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -21,10 +21,10 @@ static void validateConfig(lua_State* L, const luarequire_Configuration& config)
|
|||
luaL_error(L, "require configuration is missing required function pointer: to_child");
|
||||
if (!config.is_module_present)
|
||||
luaL_error(L, "require configuration is missing required function pointer: is_module_present");
|
||||
if (!config.get_contents)
|
||||
luaL_error(L, "require configuration is missing required function pointer: get_contents");
|
||||
if (!config.get_chunkname)
|
||||
luaL_error(L, "require configuration is missing required function pointer: get_chunkname");
|
||||
if (!config.get_loadname)
|
||||
luaL_error(L, "require configuration is missing required function pointer: get_loadname");
|
||||
if (!config.get_cache_key)
|
||||
luaL_error(L, "require configuration is missing required function pointer: get_cache_key");
|
||||
if (!config.is_config_present)
|
||||
|
|
|
@ -29,8 +29,8 @@ struct ResolvedRequire
|
|||
};
|
||||
|
||||
Status status;
|
||||
std::string contents;
|
||||
std::string chunkname;
|
||||
std::string loadname;
|
||||
std::string cacheKey;
|
||||
};
|
||||
|
||||
|
@ -89,17 +89,17 @@ static ResolvedRequire resolveRequire(luarequire_Configuration* lrc, lua_State*
|
|||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
||||
}
|
||||
|
||||
std::optional<std::string> contents = navigationContext.getContents();
|
||||
if (!contents)
|
||||
std::optional<std::string> loadname = navigationContext.getLoadname();
|
||||
if (!loadname)
|
||||
{
|
||||
errorHandler.reportError("could not get contents for module");
|
||||
errorHandler.reportError("could not get loadname for module");
|
||||
return ResolvedRequire{ResolvedRequire::Status::ErrorReported};
|
||||
}
|
||||
|
||||
return ResolvedRequire{
|
||||
ResolvedRequire::Status::ModuleRead,
|
||||
std::move(*contents),
|
||||
std::move(*chunkname),
|
||||
std::move(*loadname),
|
||||
std::move(*cacheKey),
|
||||
};
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ int lua_requireinternal(lua_State* L, const char* requirerChunkname)
|
|||
lua_pushinteger(L, numArgsBeforeLoad);
|
||||
lua_insert(L, 1);
|
||||
|
||||
int numResults = lrc->load(L, ctx, path, resolvedRequire.chunkname.c_str(), resolvedRequire.contents.c_str());
|
||||
int numResults = lrc->load(L, ctx, path, resolvedRequire.chunkname.c_str(), resolvedRequire.loadname.c_str());
|
||||
if (numResults == -1)
|
||||
{
|
||||
if (lua_gettop(L) != numArgsBeforeLoad)
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauLibWhereErrorAutoreserve)
|
||||
LUAU_FASTFLAG(LuauYieldableContinuations)
|
||||
|
||||
// convert a stack index to positive
|
||||
|
@ -80,9 +79,7 @@ void luaL_where(lua_State* L, int level)
|
|||
return;
|
||||
}
|
||||
|
||||
if (FFlag::LuauLibWhereErrorAutoreserve)
|
||||
lua_rawcheckstack(L, 1);
|
||||
|
||||
lua_rawcheckstack(L, 1);
|
||||
lua_pushliteral(L, ""); // else, no information available...
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
|
||||
|
@ -505,28 +504,16 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
|
|||
{
|
||||
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
||||
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst && FFlag::LuauAstTypeGroup3)
|
||||
if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,22 - 0,36","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,53 - 0,55","typeList":{"type":"AstTypeList","types":[]}}}}]},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else if (FFlag::LuauStoreReturnTypesAsPackOnAst)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,22 - 0,36","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}]}}},{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,53 - 0,55","typeList":{"type":"AstTypeList","types":[]}}}]},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else if (FFlag::LuauAstTypeGroup3)
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}}]},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string_view expected =
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}]}},{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}]},"exported":false})";
|
||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}}]},"exported":false})";
|
||||
CHECK(toJson(statement) == expected);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ extern int optimizationLevel;
|
|||
void luaC_fullgc(lua_State* L);
|
||||
void luaC_validate(lua_State* L);
|
||||
|
||||
LUAU_FASTFLAG(LuauLibWhereErrorAutoreserve)
|
||||
LUAU_FASTFLAG(DebugLuauAbortingChecks)
|
||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauStringFormatFixC)
|
||||
|
@ -1942,8 +1941,6 @@ int slowlyOverflowStack(lua_State* L)
|
|||
|
||||
TEST_CASE("ApiStack")
|
||||
{
|
||||
ScopedFastFlag luauLibWhereErrorAutoreserve{FFlag::LuauLibWhereErrorAutoreserve, true};
|
||||
|
||||
StateRef globalState(luaL_newstate(), lua_close);
|
||||
lua_State* L = globalState.get();
|
||||
|
||||
|
|
|
@ -33,12 +33,12 @@ LUAU_FASTFLAG(LuauClonedTableAndFunctionTypesMustHaveScopes)
|
|||
LUAU_FASTFLAG(LuauDisableNewSolverAssertsInMixedMode)
|
||||
LUAU_FASTFLAG(LuauCloneTypeAliasBindings)
|
||||
LUAU_FASTFLAG(LuauDoNotClonePersistentBindings)
|
||||
LUAU_FASTFLAG(LuauIncrementalAutocompleteDemandBasedCloning)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAG(LuauBetterScopeSelection)
|
||||
LUAU_FASTFLAG(LuauBlockDiffFragmentSelection)
|
||||
LUAU_FASTFLAG(LuauFragmentAcMemoryLeak)
|
||||
LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation)
|
||||
LUAU_FASTFLAG(LuauFragmentAutocompleteIfRecommendations)
|
||||
|
||||
static std::optional<AutocompleteEntryMap> nullCallback(std::string tag, std::optional<const ExternType*> ptr, std::optional<std::string> contents)
|
||||
{
|
||||
|
@ -73,12 +73,12 @@ struct FragmentAutocompleteFixtureImpl : BaseType
|
|||
ScopedFastFlag luauDisableNewSolverAssertsInMixedMode{FFlag::LuauDisableNewSolverAssertsInMixedMode, true};
|
||||
ScopedFastFlag luauCloneTypeAliasBindings{FFlag::LuauCloneTypeAliasBindings, true};
|
||||
ScopedFastFlag luauDoNotClonePersistentBindings{FFlag::LuauDoNotClonePersistentBindings, true};
|
||||
ScopedFastFlag luauIncrementalAutocompleteDemandBasedCloning{FFlag::LuauIncrementalAutocompleteDemandBasedCloning, true};
|
||||
ScopedFastFlag luauBetterScopeSelection{FFlag::LuauBetterScopeSelection, true};
|
||||
ScopedFastFlag luauBlockDiffFragmentSelection{FFlag::LuauBlockDiffFragmentSelection, true};
|
||||
ScopedFastFlag luauAutocompleteUsesModuleForTypeCompatibility{FFlag::LuauAutocompleteUsesModuleForTypeCompatibility, true};
|
||||
ScopedFastFlag luauFragmentAcMemoryLeak{FFlag::LuauFragmentAcMemoryLeak, true};
|
||||
ScopedFastFlag luauGlobalVariableModuleIsolation{FFlag::LuauGlobalVariableModuleIsolation, true};
|
||||
ScopedFastFlag luauFragmentAutocompleteIfRecommendations{FFlag::LuauFragmentAutocompleteIfRecommendations, true};
|
||||
|
||||
FragmentAutocompleteFixtureImpl()
|
||||
: BaseType(true)
|
||||
|
@ -746,11 +746,10 @@ TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "if_partial")
|
|||
{
|
||||
auto region = getAutocompleteRegion(
|
||||
R"(
|
||||
if
|
||||
)",
|
||||
Position{1, 3}
|
||||
if)",
|
||||
Position{1, 2}
|
||||
);
|
||||
CHECK_EQ(Location{{1, 0}, {1, 3}}, region.fragmentLocation);
|
||||
CHECK_EQ(Location{{1, 2}, {1, 2}}, region.fragmentLocation);
|
||||
REQUIRE(region.parentBlock);
|
||||
CHECK(region.nearestStatement->as<AstStatIf>());
|
||||
}
|
||||
|
@ -763,7 +762,7 @@ if true
|
|||
)",
|
||||
Position{1, 7}
|
||||
);
|
||||
CHECK_EQ(Location{{1, 0}, {1, 7}}, region.fragmentLocation);
|
||||
CHECK_EQ(Location{{1, 3}, {1, 7}}, region.fragmentLocation);
|
||||
REQUIRE(region.parentBlock);
|
||||
CHECK(region.nearestStatement->as<AstStatIf>());
|
||||
}
|
||||
|
@ -776,7 +775,7 @@ if true
|
|||
)",
|
||||
Position{1, 8}
|
||||
);
|
||||
CHECK_EQ(Location{{1, 8}, {1, 8}}, region.fragmentLocation);
|
||||
CHECK_EQ(Location{{1, 3}, {1, 8}}, region.fragmentLocation);
|
||||
REQUIRE(region.parentBlock);
|
||||
CHECK(region.nearestStatement->as<AstStatIf>());
|
||||
}
|
||||
|
@ -829,7 +828,7 @@ TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "if_else_if")
|
|||
auto region = getAutocompleteRegion(
|
||||
R"(
|
||||
if true then
|
||||
elseif
|
||||
elseif
|
||||
end
|
||||
|
||||
)",
|
||||
|
@ -849,7 +848,7 @@ elseif
|
|||
)",
|
||||
Position{2, 8}
|
||||
);
|
||||
CHECK_EQ(Location{{2, 0}, {2, 8}}, region.fragmentLocation);
|
||||
CHECK_EQ(Location{{2, 8}, {2, 8}}, region.fragmentLocation);
|
||||
REQUIRE(region.parentBlock);
|
||||
CHECK(region.nearestStatement->as<AstStatIf>());
|
||||
}
|
||||
|
@ -1071,7 +1070,7 @@ TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "statement_in_empty_fragment_is_n
|
|||
);
|
||||
REQUIRE(fragment.has_value());
|
||||
CHECK_EQ("", fragment->fragmentToParse);
|
||||
CHECK_EQ(2, fragment->ancestry.size());
|
||||
CHECK_EQ(1, fragment->ancestry.size());
|
||||
REQUIRE(fragment->root);
|
||||
CHECK_EQ(0, fragment->root->body.size);
|
||||
auto statBody = fragment->root->as<AstStatBlock>();
|
||||
|
@ -1104,7 +1103,7 @@ local z = x + y
|
|||
CHECK_EQ(Location{Position{3, 0}, Position{3, 15}}, fragment->root->location);
|
||||
|
||||
CHECK_EQ("local z = x + y", fragment->fragmentToParse);
|
||||
CHECK_EQ(5, fragment->ancestry.size());
|
||||
CHECK_EQ(4, fragment->ancestry.size());
|
||||
REQUIRE(fragment->root);
|
||||
CHECK_EQ(1, fragment->root->body.size);
|
||||
auto stat = fragment->root->body.data[0]->as<AstStatLocal>();
|
||||
|
@ -1149,7 +1148,7 @@ local y = 5
|
|||
REQUIRE(fragment.has_value());
|
||||
|
||||
CHECK_EQ("local z = x + y", fragment->fragmentToParse);
|
||||
CHECK_EQ(5, fragment->ancestry.size());
|
||||
CHECK_EQ(4, fragment->ancestry.size());
|
||||
REQUIRE(fragment->root);
|
||||
CHECK_EQ(Location{Position{2, 0}, Position{2, 15}}, fragment->root->location);
|
||||
CHECK_EQ(1, fragment->root->body.size);
|
||||
|
@ -1244,7 +1243,7 @@ abc("bar")
|
|||
|
||||
REQUIRE(stringFragment.has_value());
|
||||
|
||||
CHECK_EQ("abc(\"foo\")", stringFragment->fragmentToParse);
|
||||
CHECK_EQ("abc(\"foo\"", stringFragment->fragmentToParse);
|
||||
CHECK(stringFragment->nearestStatement);
|
||||
CHECK(stringFragment->nearestStatement->is<AstStatExpr>());
|
||||
|
||||
|
@ -3605,6 +3604,126 @@ end)
|
|||
)";
|
||||
autocompleteFragmentInBothSolvers(source, dest, Position{5, 11}, [](auto& result) {});
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "if_cond_no_then_recs_then")
|
||||
{
|
||||
const std::string source = R"(
|
||||
|
||||
)";
|
||||
|
||||
const std::string dest = R"(
|
||||
if x t
|
||||
)";
|
||||
|
||||
autocompleteFragmentInBothSolvers(
|
||||
source,
|
||||
dest,
|
||||
Position{1, 6},
|
||||
[](auto& result)
|
||||
{
|
||||
REQUIRE(result.result);
|
||||
CHECK(result.result->acResults.entryMap.count("then"));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "if_then_recs_else")
|
||||
{
|
||||
const std::string source = R"(
|
||||
if x then
|
||||
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string dest = R"(
|
||||
if x then
|
||||
e
|
||||
end
|
||||
)";
|
||||
|
||||
autocompleteFragmentInBothSolvers(
|
||||
source,
|
||||
dest,
|
||||
Position{2, 1},
|
||||
[](auto& result)
|
||||
{
|
||||
REQUIRE(result.result);
|
||||
CHECK(result.result->acResults.entryMap.count("else"));
|
||||
CHECK(result.result->acResults.entryMap.count("elseif"));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "if_else_if_table_prop_recs_no_then")
|
||||
{
|
||||
const std::string source = R"(
|
||||
type T = {xa : number, y : number}
|
||||
local t : T = {xa = 3, y = 3}
|
||||
|
||||
if t.x then
|
||||
elseif
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string dest = R"(
|
||||
type T = {xa : number, y : number}
|
||||
local t : T = {xa = 3, y = 3}
|
||||
|
||||
if t.x then
|
||||
elseif t.xa t
|
||||
end
|
||||
)";
|
||||
|
||||
autocompleteFragmentInBothSolvers(
|
||||
source,
|
||||
dest,
|
||||
Position{5, 13},
|
||||
[](auto& result)
|
||||
{
|
||||
REQUIRE(result.result);
|
||||
CHECK(!result.result->acResults.entryMap.empty());
|
||||
CHECK(!result.result->acResults.entryMap.count("xa"));
|
||||
CHECK(!result.result->acResults.entryMap.count("y"));
|
||||
CHECK(result.result->acResults.entryMap.count("then"));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "if_else_if_table_prop_recs_with_then")
|
||||
{
|
||||
const std::string source = R"(
|
||||
type T = {xa : number, y : number}
|
||||
local t : T = {xa = 3, y = 3}
|
||||
|
||||
if t.x then
|
||||
elseif then
|
||||
end
|
||||
)";
|
||||
|
||||
const std::string dest = R"(
|
||||
type T = {xa : number, y : number}
|
||||
local t : T = {xa = 3, y = 3}
|
||||
|
||||
if t.x then
|
||||
elseif t. then
|
||||
end
|
||||
)";
|
||||
|
||||
autocompleteFragmentInBothSolvers(
|
||||
source,
|
||||
dest,
|
||||
Position{5, 9},
|
||||
[](auto& result)
|
||||
{
|
||||
REQUIRE(result.result);
|
||||
CHECK(!result.result->acResults.entryMap.empty());
|
||||
CHECK(result.result->acResults.entryMap.count("xa"));
|
||||
CHECK(result.result->acResults.entryMap.count("y"));
|
||||
CHECK(!result.result->acResults.entryMap.count("then"));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// NOLINTEND(bugprone-unchecked-optional-access)
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -16,40 +16,10 @@ using namespace Luau;
|
|||
LUAU_FASTFLAG(LuauSolverV2);
|
||||
LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct NaiveModuleResolver : ModuleResolver
|
||||
{
|
||||
std::optional<ModuleInfo> resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override
|
||||
{
|
||||
if (auto name = pathExprToModuleName(currentModuleName, pathExpr))
|
||||
return {{*name, false}};
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const ModulePtr getModule(const ModuleName& moduleName) const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool moduleExists(const ModuleName& moduleName) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string getHumanReadableModuleName(const ModuleName& moduleName) const override
|
||||
{
|
||||
return moduleName;
|
||||
}
|
||||
};
|
||||
|
||||
NaiveModuleResolver naiveModuleResolver;
|
||||
|
||||
struct NaiveFileResolver : NullFileResolver
|
||||
{
|
||||
std::optional<ModuleInfo> resolveModule(const ModuleInfo* context, AstExpr* expr) override
|
||||
|
@ -920,7 +890,7 @@ TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_f
|
|||
// When this test fails, it is because the TypeIds needed by the error have been deallocated.
|
||||
// It is thus basically impossible to predict what will happen when this assert is evaluated.
|
||||
// It could segfault, or you could see weird type names like the empty string or <VALUELESS BY EXCEPTION>
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
REQUIRE_EQ(
|
||||
"Type\n\t"
|
||||
|
@ -930,14 +900,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "it_should_be_safe_to_stringify_errors_when_f
|
|||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
REQUIRE_EQ(
|
||||
R"(Type
|
||||
'{ count: string }'
|
||||
could not be converted into
|
||||
'{ Count: number }')",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
else
|
||||
REQUIRE_EQ(
|
||||
"Table type 'a' not compatible with type '{| Count: number |}' because the former is missing field 'Count'", toString(result.errors[0])
|
||||
|
|
|
@ -1628,7 +1628,7 @@ static void checkDeprecatedWarning(const Luau::LintWarning& warning, const Luau:
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "DeprecatedAttribute")
|
||||
{
|
||||
ScopedFastFlag sff[] = {{FFlag::LuauDeprecatedAttribute, true}, {FFlag::LuauSolverV2, true}};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
// @deprecated works on local functions
|
||||
{
|
||||
|
@ -1877,7 +1877,7 @@ end
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "DeprecatedAttributeFunctionDeclaration")
|
||||
{
|
||||
ScopedFastFlag sff[] = {{FFlag::LuauDeprecatedAttribute, true}, {FFlag::LuauSolverV2, true}};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
// @deprecated works on function type declarations
|
||||
|
||||
|
@ -1895,7 +1895,7 @@ bar(2)
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "DeprecatedAttributeTableDeclaration")
|
||||
{
|
||||
ScopedFastFlag sff[] = {{FFlag::LuauDeprecatedAttribute, true}, {FFlag::LuauSolverV2, true}};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
// @deprecated works on table type declarations
|
||||
|
||||
|
@ -1915,7 +1915,7 @@ print(Hooty:tooty(2.0))
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "DeprecatedAttributeMethodDeclaration")
|
||||
{
|
||||
ScopedFastFlag sff[] = {{FFlag::LuauDeprecatedAttribute, true}, {FFlag::LuauSolverV2, true}};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
// @deprecated works on table type declarations
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@ LUAU_FASTINT(LuauRecursionLimit)
|
|||
LUAU_FASTINT(LuauTypeLengthLimit)
|
||||
LUAU_FASTINT(LuauParseErrorLimit)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauParseStringIndexer)
|
||||
LUAU_FASTFLAG(LuauFixFunctionWithAttributesStartLocation)
|
||||
|
@ -457,10 +455,7 @@ TEST_CASE_FIXTURE(Fixture, "return_type_is_an_intersection_type_if_led_with_one_
|
|||
returnAnnotation = annotation->returnTypes_DEPRECATED.types.data[0]->as<AstTypeIntersection>();
|
||||
}
|
||||
REQUIRE(returnAnnotation != nullptr);
|
||||
if (FFlag::LuauAstTypeGroup3)
|
||||
CHECK(returnAnnotation->types.data[0]->as<AstTypeGroup>());
|
||||
else
|
||||
CHECK(returnAnnotation->types.data[0]->as<AstTypeReference>());
|
||||
CHECK(returnAnnotation->types.data[0]->as<AstTypeGroup>());
|
||||
CHECK(returnAnnotation->types.data[1]->as<AstTypeFunction>());
|
||||
}
|
||||
|
||||
|
@ -2780,8 +2775,6 @@ TEST_CASE_FIXTURE(Fixture, "leading_union_intersection_with_single_type_preserve
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_simple_ast_type_group")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauAstTypeGroup3, true};
|
||||
|
||||
AstStatBlock* stat = parse(R"(
|
||||
type Foo = (string)
|
||||
)");
|
||||
|
@ -2798,8 +2791,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_simple_ast_type_group")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_nested_ast_type_group")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauAstTypeGroup3, true};
|
||||
|
||||
AstStatBlock* stat = parse(R"(
|
||||
type Foo = ((string))
|
||||
)");
|
||||
|
@ -2819,8 +2810,6 @@ TEST_CASE_FIXTURE(Fixture, "parse_nested_ast_type_group")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_return_type_ast_type_group")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauAstTypeGroup3, true};
|
||||
|
||||
AstStatBlock* stat = parse(R"(
|
||||
type Foo = () -> (string)
|
||||
)");
|
||||
|
@ -4206,18 +4195,10 @@ TEST_CASE_FIXTURE(Fixture, "grouped_function_type")
|
|||
auto unionTy = paramTy.type->as<AstTypeUnion>();
|
||||
LUAU_ASSERT(unionTy);
|
||||
CHECK_EQ(unionTy->types.size, 2);
|
||||
if (FFlag::LuauAstTypeGroup3)
|
||||
{
|
||||
auto groupTy = unionTy->types.data[0]->as<AstTypeGroup>(); // (() -> ())
|
||||
REQUIRE(groupTy);
|
||||
CHECK(groupTy->type->is<AstTypeFunction>()); // () -> ()
|
||||
}
|
||||
else
|
||||
CHECK(unionTy->types.data[0]->is<AstTypeFunction>()); // () -> ()
|
||||
if (FFlag::LuauParseOptionalAsNode2)
|
||||
CHECK(unionTy->types.data[1]->is<AstTypeOptional>()); // ?
|
||||
else
|
||||
CHECK(unionTy->types.data[1]->is<AstTypeReference>()); // nil
|
||||
auto groupTy = unionTy->types.data[0]->as<AstTypeGroup>(); // (() -> ())
|
||||
REQUIRE(groupTy);
|
||||
CHECK(groupTy->type->is<AstTypeFunction>()); // () -> ()
|
||||
CHECK(unionTy->types.data[1]->is<AstTypeOptional>()); // ?
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty")
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "lualib.h"
|
||||
|
||||
#include "Luau/Repl.h"
|
||||
#include "Luau/ReplRequirer.h"
|
||||
#include "Luau/Require.h"
|
||||
#include "Luau/FileUtils.h"
|
||||
|
||||
|
@ -546,6 +547,16 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RegisterRuntimeModule")
|
|||
assertOutputContainsAll({"true"});
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "ProxyRequire")
|
||||
{
|
||||
luarequire_pushproxyrequire(L, requireConfigInit, createCliRequireContext(L));
|
||||
lua_setglobal(L, "proxyrequire");
|
||||
|
||||
std::string path = getLuauDirectory(PathType::Relative) + "/tests/require/without_config/proxy_requirer";
|
||||
runProtectedRequire(path);
|
||||
assertOutputContainsAll({"true", "result from dependency", "required into proxy_requirer"});
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(ReplWithPathFixture, "LoadStringRelative")
|
||||
{
|
||||
runCode(L, "return pcall(function() return loadstring(\"require('a/relative/path')\")() end)");
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <initializer_list>
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauSubtypeGenericsAndNegations)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -1615,4 +1616,35 @@ TEST_CASE_FIXTURE(SubtypeFixture, "multiple_reasonings")
|
|||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(SubtypeFixture, "substitute_a_generic_for_a_negation")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauSubtypeGenericsAndNegations, true};
|
||||
|
||||
// <A, B>(x: A, y: B) -> (A & ~(false?)) | B
|
||||
// (~(false?), ~(false?)) -> (~(false?) & ~(false?)) | ~(false?)
|
||||
|
||||
TypeId aTy = arena.addType(GenericType{"A"});
|
||||
getMutable<GenericType>(aTy)->scope = moduleScope.get();
|
||||
TypeId bTy = arena.addType(GenericType{"B"});
|
||||
getMutable<GenericType>(bTy)->scope = moduleScope.get();
|
||||
|
||||
TypeId genericFunctionTy = arena.addType(FunctionType{
|
||||
{aTy, bTy},
|
||||
{},
|
||||
arena.addTypePack({aTy, bTy}),
|
||||
arena.addTypePack({join(meet(aTy, builtinTypes->truthyType), bTy)})
|
||||
});
|
||||
|
||||
const TypeId truthyTy = builtinTypes->truthyType;
|
||||
|
||||
TypeId actualFunctionTy = fn(
|
||||
{truthyTy, truthyTy},
|
||||
{join(meet(truthyTy, builtinTypes->truthyType), truthyTy)}
|
||||
);
|
||||
|
||||
SubtypingResult result = isSubtype(genericFunctionTy, actualFunctionTy);
|
||||
|
||||
CHECK(result.isSubtype);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -14,7 +14,6 @@ using namespace Luau;
|
|||
LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauAttributeSyntax)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
|
||||
TEST_SUITE_BEGIN("ToString");
|
||||
|
||||
|
@ -873,15 +872,12 @@ TEST_CASE_FIXTURE(Fixture, "tostring_error_mismatch")
|
|||
)");
|
||||
|
||||
std::string expected;
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
expected =
|
||||
"Type pack '{ a: number, b: string, c: { d: string } }' could not be converted into '{ a: number, b: string, c: { d: number } }'; \n"
|
||||
"this is because in the 1st entry in the type pack, accessing `c.d` results in `string` in the former type and `number` in the latter "
|
||||
"type, and `string` is not exactly `number`";
|
||||
else if (FFlag::LuauSolverV2)
|
||||
expected =
|
||||
R"(Type pack '{ a: number, b: string, c: { d: string } }' could not be converted into '{ a: number, b: string, c: { d: number } }'; at [0][read "c"][read "d"], string is not exactly number)";
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
expected = R"(Type
|
||||
'{ a: number, b: string, c: { d: string } }'
|
||||
could not be converted into
|
||||
|
@ -895,20 +891,6 @@ could not be converted into
|
|||
caused by:
|
||||
Property 'd' is not compatible.
|
||||
Type 'string' could not be converted into 'number' in an invariant context)";
|
||||
else
|
||||
expected = R"(Type
|
||||
'{ a: number, b: string, c: { d: string } }'
|
||||
could not be converted into
|
||||
'{| a: number, b: string, c: {| d: number |} |}'
|
||||
caused by:
|
||||
Property 'c' is not compatible.
|
||||
Type
|
||||
'{ d: string }'
|
||||
could not be converted into
|
||||
'{| d: number |}'
|
||||
caused by:
|
||||
Property 'd' is not compatible.
|
||||
Type 'string' could not be converted into 'number' in an invariant context)";
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
|
|
|
@ -13,9 +13,8 @@
|
|||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauStoreCSTData2)
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup3);
|
||||
LUAU_FASTFLAG(LuauParseOptionalAsNode2)
|
||||
LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst)
|
||||
LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions)
|
||||
|
||||
TEST_SUITE_BEGIN("TranspilerTests");
|
||||
|
||||
|
@ -343,7 +342,8 @@ TEST_CASE("function_with_types_spaces_around_tokens")
|
|||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true}
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
{FFlag::LuauStoreLocalAnnotationColonPositions, true},
|
||||
};
|
||||
std::string code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -369,9 +369,8 @@ TEST_CASE("function_with_types_spaces_around_tokens")
|
|||
code = R"( function p<X, Y, Z...> (o: string, m: number, ...: any): string end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
// TODO(CLI-139347): re-enable test once colon positions are supported
|
||||
// code = R"( function p<X, Y, Z...>(o : string, m: number, ...: any): string end )";
|
||||
// CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
code = R"( function p<X, Y, Z...>(o : string, m: number, ...: any): string end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -385,9 +384,8 @@ TEST_CASE("function_with_types_spaces_around_tokens")
|
|||
code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
// TODO(CLI-139347): re-enable test once colon positions are supported
|
||||
// code = R"( function p<X, Y, Z...>(o: string, m: number, ... : any): string end )";
|
||||
// CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
code = R"( function p<X, Y, Z...>(o: string, m: number, ... : any): string end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( function p<X, Y, Z...>(o: string, m: number, ...: any): string end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -1249,6 +1247,59 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens")
|
|||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreLocalAnnotationColonPositions, true},
|
||||
};
|
||||
std::string code = R"( local _: Type )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( local _ : Type )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( local _: Type )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( local x: Type, y = 1 )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( local x : Type, y = 1 )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_for_loop_annotation_spaces_around_tokens")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauStoreLocalAnnotationColonPositions, true},
|
||||
};
|
||||
std::string code = R"( for i: number = 1, 10 do end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( for i : number = 1, 10 do end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( for i: number = 1, 10 do end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( for x: number, y: number in ... do end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( for x : number, y: number in ... do end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( for x: number, y: number in ... do end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( for x: number, y : number in ... do end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
||||
code = R"( for x: number, y: number in ... do end )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_type_packs")
|
||||
{
|
||||
std::string code = R"(
|
||||
|
@ -1323,10 +1374,8 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3")
|
|||
|
||||
if (FFlag::LuauStoreCSTData2)
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
else if (FFlag::LuauAstTypeGroup3)
|
||||
CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code);
|
||||
else
|
||||
CHECK_EQ("local a: ( string & number)?", transpile(code, {}, true).code);
|
||||
CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested")
|
||||
|
@ -1358,7 +1407,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe")
|
|||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauParseOptionalAsNode2, true},
|
||||
};
|
||||
std::string code = "local a: | string | number";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -1371,7 +1419,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens")
|
|||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauParseOptionalAsNode2, true},
|
||||
};
|
||||
std::string code = "local a: string | number";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -1408,8 +1455,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection")
|
|||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauAstTypeGroup3, true},
|
||||
{FFlag::LuauParseOptionalAsNode2, true},
|
||||
};
|
||||
std::string code = "local a: string | (Foo & Bar)";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -1437,7 +1482,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_preserve_union_optional_style")
|
|||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauParseOptionalAsNode2, true},
|
||||
};
|
||||
std::string code = "local a: string | nil";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
|
@ -2029,7 +2073,6 @@ TEST_CASE("transpile_types_preserve_parentheses_style")
|
|||
{
|
||||
ScopedFastFlag flags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauAstTypeGroup3, true},
|
||||
};
|
||||
|
||||
std::string code = R"( type Foo = number )";
|
||||
|
@ -2176,7 +2219,6 @@ TEST_CASE("transpile_type_function_return_types")
|
|||
{
|
||||
ScopedFastFlag fflags[] = {
|
||||
{FFlag::LuauStoreCSTData2, true},
|
||||
{FFlag::LuauAstTypeGroup3, true},
|
||||
{FFlag::LuauStoreReturnTypesAsPackOnAst, true},
|
||||
};
|
||||
std::string code = R"( type Foo = () -> () )";
|
||||
|
@ -2224,8 +2266,6 @@ TEST_CASE("transpile_type_function_return_types")
|
|||
|
||||
TEST_CASE("fuzzer_nil_optional")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauParseOptionalAsNode2, true};
|
||||
|
||||
const std::string code = R"( local x: nil? )";
|
||||
CHECK_EQ(code, transpile(code, {}, true).code);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ using namespace Luau;
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauTypeFunReadWriteParents)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauUserTypeFunTypecheck)
|
||||
LUAU_FASTFLAG(LuauNewTypeFunReductionChecks2)
|
||||
LUAU_FASTFLAG(LuauNoTypeFunctionsNamedTypeOf)
|
||||
|
@ -1337,35 +1336,21 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
|
||||
CHECK(
|
||||
toString(result.errors[0]) ==
|
||||
"Type pack '\"number\"' could not be converted into 'never'; \n"
|
||||
R"(this is because the 1st entry in the type pack is `"number"` in the former type and `never` in the latter type, and `"number"` is not a subtype of `never`)"
|
||||
);
|
||||
CHECK(
|
||||
toString(result.errors[1]) ==
|
||||
"Type pack '\"string\"' could not be converted into 'never'; \n"
|
||||
R"(this is because the 1st entry in the type pack is `"string"` in the former type and `never` in the latter type, and `"string"` is not a subtype of `never`)"
|
||||
);
|
||||
CHECK(
|
||||
toString(result.errors[2]) ==
|
||||
"Type pack '\"table\"' could not be converted into 'never'; \n"
|
||||
R"(this is because the 1st entry in the type pack is `"table"` in the former type and `never` in the latter type, and `"table"` is not a subtype of `never`)"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(
|
||||
toString(result.errors[0]) == R"(Type pack '"number"' could not be converted into 'never'; at [0], "number" is not a subtype of never)"
|
||||
);
|
||||
CHECK(
|
||||
toString(result.errors[1]) == R"(Type pack '"string"' could not be converted into 'never'; at [0], "string" is not a subtype of never)"
|
||||
);
|
||||
CHECK(toString(result.errors[2]) == R"(Type pack '"table"' could not be converted into 'never'; at [0], "table" is not a subtype of never)");
|
||||
}
|
||||
CHECK(
|
||||
toString(result.errors[0]) ==
|
||||
"Type pack '\"number\"' could not be converted into 'never'; \n"
|
||||
R"(this is because the 1st entry in the type pack is `"number"` in the former type and `never` in the latter type, and `"number"` is not a subtype of `never`)"
|
||||
);
|
||||
CHECK(
|
||||
toString(result.errors[1]) ==
|
||||
"Type pack '\"string\"' could not be converted into 'never'; \n"
|
||||
R"(this is because the 1st entry in the type pack is `"string"` in the former type and `never` in the latter type, and `"string"` is not a subtype of `never`)"
|
||||
);
|
||||
CHECK(
|
||||
toString(result.errors[2]) ==
|
||||
"Type pack '\"table\"' could not be converted into 'never'; \n"
|
||||
R"(this is because the 1st entry in the type pack is `"table"` in the former type and `never` in the latter type, and `"table"` is not a subtype of `never`)"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_serialization")
|
||||
|
|
|
@ -11,10 +11,9 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauFixInfiniteRecursionInNormalization)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
LUAU_FASTFLAG(LuauRetainDefinitionAliasLocations)
|
||||
LUAU_FASTFLAG(LuauNewNonStrictVisitTypes2)
|
||||
LUAU_FASTFLAG(LuauGuardAgainstMalformedTypeAliasExpansion)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeAliases");
|
||||
|
||||
|
@ -220,11 +219,10 @@ TEST_CASE_FIXTURE(Fixture, "generic_aliases")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors)
|
||||
? "Type '{ v: string }' could not be converted into 'T<number>'; \n"
|
||||
"this is because accessing `v` results in `string` in the former type and `number` in the latter type, and "
|
||||
"`string` is not exactly `number`"
|
||||
: R"(Type '{ v: string }' could not be converted into 'T<number>'; at [read "v"], string is not exactly number)";
|
||||
const std::string expected =
|
||||
"Type '{ v: string }' could not be converted into 'T<number>'; \n"
|
||||
"this is because accessing `v` results in `string` in the former type and `number` in the latter type, and "
|
||||
"`string` is not exactly `number`";
|
||||
CHECK(result.errors[0].location == Location{{4, 31}, {4, 44}});
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
@ -244,11 +242,9 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
(FFlag::LuauImproveTypePathsInErrors)
|
||||
? "Type '{ t: { v: string } }' could not be converted into 'U<number>'; \n"
|
||||
"this is because accessing `t.v` results in `string` in the former type and `number` in the latter type, and `string` is not exactly "
|
||||
"`number`"
|
||||
: R"(Type '{ t: { v: string } }' could not be converted into 'U<number>'; at [read "t"][read "v"], string is not exactly number)";
|
||||
"Type '{ t: { v: string } }' could not be converted into 'U<number>'; \n"
|
||||
"this is because accessing `t.v` results in `string` in the former type and `number` in the latter type, and `string` is not exactly "
|
||||
"`number`";
|
||||
|
||||
CHECK(result.errors[0].location == Location{{4, 31}, {4, 52}});
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
|
@ -256,8 +252,6 @@ TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
type T<a> = { f: a, g: U<a> }
|
||||
|
@ -1268,4 +1262,17 @@ TEST_CASE_FIXTURE(Fixture, "exported_type_function_location_is_accessible_on_mod
|
|||
CHECK_EQ(tfun->second.definitionLocation, Location{{1, 8}, {2, 11}});
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "fuzzer_cursed_type_aliases")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauGuardAgainstMalformedTypeAliasExpansion, true};
|
||||
|
||||
// This used to crash under the new solver: we would like this to continue
|
||||
// to not crash.
|
||||
LUAU_REQUIRE_ERRORS(check(R"(
|
||||
export type t1<t0...> = t4<t0...>
|
||||
export type t4<t2, t0...> = t4<t0...>
|
||||
)"));
|
||||
}
|
||||
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -11,7 +11,6 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauTableCloneClonesType3)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||
|
||||
|
@ -147,32 +146,20 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "sort_with_bad_predicate")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors) ? "Type\n\t"
|
||||
"'(number, number) -> boolean'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((string, string) -> boolean)?'"
|
||||
"\ncaused by:\n"
|
||||
" None of the union options are compatible. For example:\n"
|
||||
"Type\n\t"
|
||||
"'(number, number) -> boolean'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string, string) -> boolean'"
|
||||
"\ncaused by:\n"
|
||||
" Argument #1 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'"
|
||||
: R"(Type
|
||||
'(number, number) -> boolean'
|
||||
could not be converted into
|
||||
'((string, string) -> boolean)?'
|
||||
caused by:
|
||||
None of the union options are compatible. For example:
|
||||
Type
|
||||
'(number, number) -> boolean'
|
||||
could not be converted into
|
||||
'(string, string) -> boolean'
|
||||
caused by:
|
||||
Argument #1 type is not compatible.
|
||||
Type 'string' could not be converted into 'number')";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> boolean'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((string, string) -> boolean)?'"
|
||||
"\ncaused by:\n"
|
||||
" None of the union options are compatible. For example:\n"
|
||||
"Type\n\t"
|
||||
"'(number, number) -> boolean'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string, string) -> boolean'"
|
||||
"\ncaused by:\n"
|
||||
" Argument #1 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1008,17 +995,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tonumber_returns_optional_number_type")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
CHECK_EQ(
|
||||
"Type 'number?' could not be converted into 'number'; \n"
|
||||
"this is because the 2nd component of the union is `nil`, which is not a subtype of `number`",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK_EQ(
|
||||
"Type 'number?' could not be converted into 'number'; type number?[1] (nil) is not a subtype of number (number)",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
else
|
||||
CHECK_EQ("Type 'number?' could not be converted into 'number'", toString(result.errors[0]));
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ using namespace Luau;
|
|||
using std::nullopt;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeInferExternTypes");
|
||||
|
||||
|
@ -547,14 +546,12 @@ local b: B = a
|
|||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
CHECK(
|
||||
"Type 'A' could not be converted into 'B'; \n"
|
||||
"this is because accessing `x` results in `ChildClass` in the former type and `BaseClass` in the latter type, and `ChildClass` is not "
|
||||
"exactly `BaseClass`" == toString(result.errors.at(0))
|
||||
);
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK(toString(result.errors.at(0)) == "Type 'A' could not be converted into 'B'; at [read \"x\"], ChildClass is not exactly BaseClass");
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'A' could not be converted into 'B'
|
||||
|
|
|
@ -23,9 +23,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTINT(LuauTarjanChildLimit)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauUngeneralizedTypesForRecursiveFunctions)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauReduceUnionFollowUnionType)
|
||||
LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments)
|
||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
|
@ -1313,7 +1311,7 @@ f(function(a, b, c, ...) return a + b end)
|
|||
LUAU_REQUIRE_ERRORS(result);
|
||||
|
||||
std::string expected;
|
||||
if (FFlag::LuauInstantiateInSubtyping && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauInstantiateInSubtyping)
|
||||
{
|
||||
expected = "Type\n\t"
|
||||
"'<a>(number, number, a) -> number'"
|
||||
|
@ -1322,16 +1320,7 @@ f(function(a, b, c, ...) return a + b end)
|
|||
"\ncaused by:\n"
|
||||
" Argument count mismatch. Function expects 3 arguments, but only 2 are specified";
|
||||
}
|
||||
else if (FFlag::LuauInstantiateInSubtyping)
|
||||
{
|
||||
expected = R"(Type
|
||||
'<a>(number, number, a) -> number'
|
||||
could not be converted into
|
||||
'(number, number) -> number'
|
||||
caused by:
|
||||
Argument count mismatch. Function expects 3 arguments, but only 2 are specified)";
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
expected = "Type\n\t"
|
||||
"'(number, number, *error-type*) -> number'"
|
||||
|
@ -1340,15 +1329,6 @@ caused by:
|
|||
"\ncaused by:\n"
|
||||
" Argument count mismatch. Function expects 3 arguments, but only 2 are specified";
|
||||
}
|
||||
else
|
||||
{
|
||||
expected = R"(Type
|
||||
'(number, number, *error-type*) -> number'
|
||||
could not be converted into
|
||||
'(number, number) -> number'
|
||||
caused by:
|
||||
Argument count mismatch. Function expects 3 arguments, but only 2 are specified)";
|
||||
}
|
||||
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
|
||||
|
@ -1544,19 +1524,13 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors)
|
||||
? "Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number) -> string'"
|
||||
"\ncaused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified"
|
||||
: R"(Type
|
||||
'(number, number) -> string'
|
||||
could not be converted into
|
||||
'(number) -> string'
|
||||
caused by:
|
||||
Argument count mismatch. Function expects 2 arguments, but only 1 is specified)";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number) -> string'"
|
||||
"\ncaused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1574,20 +1548,14 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors) ? "Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, string) -> string'"
|
||||
"\ncaused by:\n"
|
||||
" Argument #2 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'"
|
||||
: R"(Type
|
||||
'(number, number) -> string'
|
||||
could not be converted into
|
||||
'(number, string) -> string'
|
||||
caused by:
|
||||
Argument #2 type is not compatible.
|
||||
Type 'string' could not be converted into 'number')";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, string) -> string'"
|
||||
"\ncaused by:\n"
|
||||
" Argument #2 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1605,18 +1573,13 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors) ? "Type\n\t"
|
||||
"'(number, number) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> (number, boolean)'"
|
||||
"\ncaused by:\n"
|
||||
" Function only returns 1 value, but 2 are required here"
|
||||
: R"(Type
|
||||
'(number, number) -> number'
|
||||
could not be converted into
|
||||
'(number, number) -> (number, boolean)'
|
||||
caused by:
|
||||
Function only returns 1 value, but 2 are required here)";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> (number, boolean)'"
|
||||
"\ncaused by:\n"
|
||||
" Function only returns 1 value, but 2 are required here";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1634,20 +1597,14 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors) ? "Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> number'"
|
||||
"\ncaused by:\n"
|
||||
" Return type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'"
|
||||
: R"(Type
|
||||
'(number, number) -> string'
|
||||
could not be converted into
|
||||
'(number, number) -> number'
|
||||
caused by:
|
||||
Return type is not compatible.
|
||||
Type 'string' could not be converted into 'number')";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> number'"
|
||||
"\ncaused by:\n"
|
||||
" Return type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'number'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1665,20 +1622,14 @@ local b: B = a
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors) ? "Type\n\t"
|
||||
"'(number, number) -> (number, string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> (number, boolean)'"
|
||||
"\ncaused by:\n"
|
||||
" Return #2 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'boolean'"
|
||||
: R"(Type
|
||||
'(number, number) -> (number, string)'
|
||||
could not be converted into
|
||||
'(number, number) -> (number, boolean)'
|
||||
caused by:
|
||||
Return #2 type is not compatible.
|
||||
Type 'string' could not be converted into 'boolean')";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, number) -> (number, string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number, number) -> (number, boolean)'"
|
||||
"\ncaused by:\n"
|
||||
" Return #2 type is not compatible.\n"
|
||||
"Type 'string' could not be converted into 'boolean'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -1828,7 +1779,7 @@ end
|
|||
R"(Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time)"
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type
|
||||
|
@ -1843,24 +1794,6 @@ could not be converted into
|
|||
'(number) -> number'
|
||||
caused by:
|
||||
Argument #1 type is not compatible.
|
||||
Type 'number' could not be converted into 'string')");
|
||||
CHECK_EQ(toString(result.errors[1]), R"(Type 'string' could not be converted into 'number')");
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type
|
||||
'(string) -> string'
|
||||
could not be converted into
|
||||
'((number) -> number)?'
|
||||
caused by:
|
||||
None of the union options are compatible. For example:
|
||||
Type
|
||||
'(string) -> string'
|
||||
could not be converted into
|
||||
'(number) -> number'
|
||||
caused by:
|
||||
Argument #1 type is not compatible.
|
||||
Type 'number' could not be converted into 'string')");
|
||||
CHECK_EQ(toString(result.errors[1]), R"(Type 'string' could not be converted into 'number')");
|
||||
}
|
||||
|
@ -1890,30 +1823,15 @@ function t:b() return 2 end -- not OK
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
CHECK_EQ(
|
||||
"Type\n\t"
|
||||
"'(*error-type*) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'() -> number'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 1 argument, but none are specified",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'(*error-type*) -> number'
|
||||
could not be converted into
|
||||
'() -> number'
|
||||
caused by:
|
||||
Argument count mismatch. Function expects 1 argument, but none are specified)",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
CHECK_EQ(
|
||||
"Type\n\t"
|
||||
"'(*error-type*) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'() -> number'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 1 argument, but none are specified",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")
|
||||
|
@ -2187,15 +2105,10 @@ z = y -- Not OK, so the line is colorable
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
(FFlag::LuauImproveTypePathsInErrors)
|
||||
? "Type\n\t"
|
||||
R"('(("blue" | "red") -> ("blue" | "red") -> ("blue" | "red") -> boolean) & (("blue" | "red") -> ("blue") -> ("blue") -> false) & (("blue" | "red") -> ("red") -> ("red") -> false) & (("blue") -> ("blue") -> ("blue" | "red") -> false) & (("red") -> ("red") -> ("blue" | "red") -> false)')"
|
||||
"\ncould not be converted into\n\t"
|
||||
R"('("blue" | "red") -> ("blue" | "red") -> ("blue" | "red") -> false'; none of the intersection parts are compatible)"
|
||||
: R"(Type
|
||||
'(("blue" | "red") -> ("blue" | "red") -> ("blue" | "red") -> boolean) & (("blue" | "red") -> ("blue") -> ("blue") -> false) & (("blue" | "red") -> ("red") -> ("red") -> false) & (("blue") -> ("blue") -> ("blue" | "red") -> false) & (("red") -> ("red") -> ("blue" | "red") -> false)'
|
||||
could not be converted into
|
||||
'("blue" | "red") -> ("blue" | "red") -> ("blue" | "red") -> false'; none of the intersection parts are compatible)";
|
||||
"Type\n\t"
|
||||
R"('(("blue" | "red") -> ("blue" | "red") -> ("blue" | "red") -> boolean) & (("blue" | "red") -> ("blue") -> ("blue") -> false) & (("blue" | "red") -> ("red") -> ("red") -> false) & (("blue") -> ("blue") -> ("blue" | "red") -> false) & (("red") -> ("red") -> ("blue" | "red") -> false)')"
|
||||
"\ncould not be converted into\n\t"
|
||||
R"('("blue" | "red") -> ("blue" | "red") -> ("blue" | "red") -> false'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
|
@ -2525,14 +2438,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_before_num_or_str")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
CHECK(
|
||||
"Type pack 'string' could not be converted into 'number'; \n"
|
||||
"this is because the 1st entry in the type pack is `string` in the former type and `number` in the latter type, and `string` is not a "
|
||||
"subtype of `number`" == toString(result.errors.at(0))
|
||||
);
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK(toString(result.errors.at(0)) == "Type pack 'string' could not be converted into 'number'; at [0], string is not a subtype of number");
|
||||
else
|
||||
CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0]));
|
||||
|
||||
|
@ -2556,14 +2467,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "num_is_solved_after_num_or_str")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
CHECK(
|
||||
"Type pack 'string' could not be converted into 'number'; \n"
|
||||
"this is because the 1st entry in the type pack is `string` in the former type and `number` in the latter type, and `string` is not a "
|
||||
"subtype of `number`" == toString(result.errors.at(0))
|
||||
);
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK(toString(result.errors.at(0)) == "Type pack 'string' could not be converted into 'number'; at [0], string is not a subtype of number");
|
||||
else
|
||||
CHECK_EQ("Type 'string' could not be converted into 'number'", toString(result.errors[0]));
|
||||
CHECK_EQ("() -> number", toString(requireType("num_or_str")));
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAG(LuauIntersectNotNil)
|
||||
|
||||
|
@ -867,12 +866,10 @@ y.a.c = y
|
|||
CHECK_EQ(toString(mismatch->givenType), "{ a: { c: T<string>?, d: number }, b: number }");
|
||||
CHECK_EQ(toString(mismatch->wantedType), "T<string>");
|
||||
std::string reason =
|
||||
(FFlag::LuauImproveTypePathsInErrors)
|
||||
? "\nthis is because \n\t"
|
||||
" * accessing `a.d` results in `number` in the former type and `string` in the latter type, and `number` is not exactly "
|
||||
"`string`\n\t"
|
||||
" * accessing `b` results in `number` in the former type and `string` in the latter type, and `number` is not exactly `string`"
|
||||
: "at [read \"a\"][read \"d\"], number is not exactly string\n\tat [read \"b\"], number is not exactly string";
|
||||
"\nthis is because \n\t"
|
||||
" * accessing `a.d` results in `number` in the former type and `string` in the latter type, and `number` is not exactly "
|
||||
"`string`\n\t"
|
||||
" * accessing `b` results in `number` in the former type and `string` in the latter type, and `number` is not exactly `string`";
|
||||
CHECK_EQ(mismatch->reason, reason);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
using namespace Luau;
|
||||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauNarrowIntersectionNevers)
|
||||
|
||||
TEST_SUITE_BEGIN("IntersectionTypes");
|
||||
|
@ -360,19 +359,13 @@ TEST_CASE_FIXTURE(Fixture, "table_intersection_write_sealed_indirect")
|
|||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors)
|
||||
? "Type\n\t"
|
||||
"'(string, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string) -> string'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified"
|
||||
: R"(Type
|
||||
'(string, number) -> string'
|
||||
could not be converted into
|
||||
'(string) -> string'
|
||||
caused by:
|
||||
Argument count mismatch. Function expects 2 arguments, but only 1 is specified)";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(string, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string) -> string'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
CHECK_EQ(toString(result.errors[1]), "Cannot add property 'z' to table 'X & Y'");
|
||||
|
@ -398,19 +391,13 @@ TEST_CASE_FIXTURE(Fixture, "table_write_sealed_indirect")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors)
|
||||
? "Type\n\t"
|
||||
"'(string, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string) -> string'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified"
|
||||
: R"(Type
|
||||
'(string, number) -> string'
|
||||
could not be converted into
|
||||
'(string) -> string'
|
||||
caused by:
|
||||
Argument count mismatch. Function expects 2 arguments, but only 1 is specified)";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(string, number) -> string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(string) -> string'\n"
|
||||
"caused by:\n"
|
||||
" Argument count mismatch. Function expects 2 arguments, but only 1 is specified";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
|
||||
CHECK_EQ(toString(result.errors[1]), "Cannot add property 'z' to table 'XY'");
|
||||
|
@ -440,31 +427,30 @@ local a: XYZ = 3
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type 'number' could not be converted into 'X & Y & Z'
|
||||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected =
|
||||
"Type "
|
||||
"'number'"
|
||||
" could not be converted into "
|
||||
"'X & Y & Z'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `X`, and `number` is not a subtype of `X`\n\t"
|
||||
" * the 2nd component of the intersection is `Y`, and `number` is not a subtype of `Y`\n\t"
|
||||
" * the 3rd component of the intersection is `Z`, and `number` is not a subtype of `Z`";
|
||||
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'number' could not be converted into 'X & Y & Z'
|
||||
caused by:
|
||||
Not all intersection parts are compatible.
|
||||
Type 'number' could not be converted into 'X')";
|
||||
const std::string dcrExprected =
|
||||
R"(Type 'number' could not be converted into 'X & Y & Z'; type number (number) is not a subtype of X & Y & Z[0] (X)
|
||||
type number (number) is not a subtype of X & Y & Z[1] (Y)
|
||||
type number (number) is not a subtype of X & Y & Z[2] (Z))";
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type "
|
||||
"'number'"
|
||||
" could not be converted into "
|
||||
"'X & Y & Z'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `X`, and `number` is not a subtype of `X`\n\t"
|
||||
" * the 2nd component of the intersection is `Y`, and `number` is not a subtype of `Y`\n\t"
|
||||
" * the 3rd component of the intersection is `Z`, and `number` is not a subtype of `Z`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK_EQ(dcrExprected, toString(result.errors[0]));
|
||||
else
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_intersection_all")
|
||||
|
@ -482,30 +468,22 @@ end
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type pack "
|
||||
"'X & Y & Z'"
|
||||
" could not be converted into "
|
||||
"'number'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st entry in the type pack has the 1st component of the intersection as `X` and the 1st entry in the "
|
||||
"type pack is `number`, and `X` is not a subtype of `number`\n\t"
|
||||
" * in the 1st entry in the type pack has the 2nd component of the intersection as `Y` and the 1st entry in the "
|
||||
"type pack is `number`, and `Y` is not a subtype of `number`\n\t"
|
||||
" * in the 1st entry in the type pack has the 3rd component of the intersection as `Z` and the 1st entry in the "
|
||||
"type pack is `number`, and `Z` is not a subtype of `number`";
|
||||
const std::string expected =
|
||||
"Type pack "
|
||||
"'X & Y & Z'"
|
||||
" could not be converted into "
|
||||
"'number'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st entry in the type pack has the 1st component of the intersection as `X` and the 1st entry in the "
|
||||
"type pack is `number`, and `X` is not a subtype of `number`\n\t"
|
||||
" * in the 1st entry in the type pack has the 2nd component of the intersection as `Y` and the 1st entry in the "
|
||||
"type pack is `number`, and `Y` is not a subtype of `number`\n\t"
|
||||
" * in the 1st entry in the type pack has the 3rd component of the intersection as `Z` and the 1st entry in the "
|
||||
"type pack is `number`, and `Z` is not a subtype of `number`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK_EQ(
|
||||
R"(Type pack 'X & Y & Z' could not be converted into 'number'; type X & Y & Z[0][0] (X) is not a subtype of number[0] (number)
|
||||
type X & Y & Z[0][1] (Y) is not a subtype of number[0] (number)
|
||||
type X & Y & Z[0][2] (Z) is not a subtype of number[0] (number))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]), R"(Type 'X & Y & Z' could not be converted into 'number'; none of the intersection parts are compatible)"
|
||||
|
@ -551,25 +529,18 @@ TEST_CASE_FIXTURE(Fixture, "intersect_bool_and_false")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type "
|
||||
"'boolean & false'"
|
||||
" could not be converted into "
|
||||
"'true'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `boolean`, which is not a subtype of `true`\n\t"
|
||||
" * the 2nd component of the intersection is `false`, which is not a subtype of `true`";
|
||||
const std::string expected =
|
||||
"Type "
|
||||
"'boolean & false'"
|
||||
" could not be converted into "
|
||||
"'true'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `boolean`, which is not a subtype of `true`\n\t"
|
||||
" * the 2nd component of the intersection is `false`, which is not a subtype of `true`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK_EQ(
|
||||
R"(Type 'boolean & false' could not be converted into 'true'; type boolean & false[0] (boolean) is not a subtype of true (true)
|
||||
type boolean & false[1] (false) is not a subtype of true (true))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]), "Type 'boolean & false' could not be converted into 'true'; none of the intersection parts are compatible"
|
||||
|
@ -588,25 +559,19 @@ TEST_CASE_FIXTURE(Fixture, "intersect_false_and_bool_and_false")
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
// TODO: odd stringification of `false & (boolean & false)`.)
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type "
|
||||
"'boolean & false & false'"
|
||||
" could not be converted into "
|
||||
"'true'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `false`, which is not a subtype of `true`\n\t"
|
||||
" * the 2nd component of the intersection is `boolean`, which is not a subtype of `true`\n\t"
|
||||
" * the 3rd component of the intersection is `false`, which is not a subtype of `true`";
|
||||
const std::string expected =
|
||||
"Type "
|
||||
"'boolean & false & false'"
|
||||
" could not be converted into "
|
||||
"'true'; \n"
|
||||
"this is because \n\t"
|
||||
" * the 1st component of the intersection is `false`, which is not a subtype of `true`\n\t"
|
||||
" * the 2nd component of the intersection is `boolean`, which is not a subtype of `true`\n\t"
|
||||
" * the 3rd component of the intersection is `false`, which is not a subtype of `true`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK_EQ(
|
||||
R"(Type 'boolean & false & false' could not be converted into 'true'; type boolean & false & false[0] (false) is not a subtype of true (true)
|
||||
type boolean & false & false[1] (boolean) is not a subtype of true (true)
|
||||
type boolean & false & false[2] (false) is not a subtype of true (true))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
else
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
|
@ -623,7 +588,7 @@ TEST_CASE_FIXTURE(Fixture, "intersect_saturate_overloaded_functions")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected1 =
|
||||
"Type\n\t"
|
||||
|
@ -654,25 +619,7 @@ TEST_CASE_FIXTURE(Fixture, "intersect_saturate_overloaded_functions")
|
|||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
const std::string expected1 = R"(Type
|
||||
'((number?) -> number?) & ((string?) -> string?)'
|
||||
could not be converted into
|
||||
'(nil) -> nil'; type ((number?) -> number?) & ((string?) -> string?)[0].returns()[0][0] (number) is not a subtype of (nil) -> nil.returns()[0] (nil)
|
||||
type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][0] (string) is not a subtype of (nil) -> nil.returns()[0] (nil))";
|
||||
const std::string expected2 = R"(Type
|
||||
'((number?) -> number?) & ((string?) -> string?)'
|
||||
could not be converted into
|
||||
'(number) -> number'; type ((number?) -> number?) & ((string?) -> string?)[0].returns()[0][1] (nil) is not a subtype of (number) -> number.returns()[0] (number)
|
||||
type ((number?) -> number?) & ((string?) -> string?)[1].arguments()[0] (string?) is not a supertype of (number) -> number.arguments()[0] (number)
|
||||
type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][0] (string) is not a subtype of (number) -> number.returns()[0] (number)
|
||||
type ((number?) -> number?) & ((string?) -> string?)[1].returns()[0][1] (nil) is not a subtype of (number) -> number.returns()[0] (number))";
|
||||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
|
@ -681,15 +628,6 @@ could not be converted into
|
|||
'(number) -> number'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((number?) -> number?) & ((string?) -> string?)'
|
||||
could not be converted into
|
||||
'(number) -> number'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union_saturate_overloaded_functions")
|
||||
|
@ -706,22 +644,13 @@ TEST_CASE_FIXTURE(Fixture, "union_saturate_overloaded_functions")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((number) -> number) & ((string) -> string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(boolean | number) -> boolean | number'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'((number) -> number) & ((string) -> string)'
|
||||
could not be converted into
|
||||
'(boolean | number) -> boolean | number'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'((number) -> number) & ((string) -> string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(boolean | number) -> boolean | number'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "intersection_of_tables")
|
||||
|
@ -735,20 +664,21 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type "
|
||||
"'{ p: number?, q: number?, r: number? } & { p: number?, q: string? }'"
|
||||
" could not be converted into "
|
||||
"'{ p: nil }'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `nil`, and `number` is not exactly `nil`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `nil`, and `number` is not exactly `nil`";
|
||||
const std::string expected =
|
||||
"Type "
|
||||
"'{ p: number?, q: number?, r: number? } & { p: number?, q: string? }'"
|
||||
" could not be converted into "
|
||||
"'{ p: nil }'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `nil`, and `number` is not exactly `nil`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `nil`, and `number` is not exactly `nil`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
const std::string expected =
|
||||
R"(Type
|
||||
|
@ -757,19 +687,6 @@ could not be converted into
|
|||
'{| p: nil |}'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected =
|
||||
(FFlag::LuauSolverV2)
|
||||
? R"(Type '{ p: number?, q: number?, r: number? } & { p: number?, q: string? }' could not be converted into '{ p: nil }'; type { p: number?, q: number?, r: number? } & { p: number?, q: string? }[0][read "p"][0] (number) is not exactly { p: nil }[read "p"] (nil)
|
||||
type { p: number?, q: number?, r: number? } & { p: number?, q: string? }[1][read "p"][0] (number) is not exactly { p: nil }[read "p"] (nil))"
|
||||
:
|
||||
R"(Type
|
||||
'{| p: number?, q: number?, r: number? |} & {| p: number?, q: string? |}'
|
||||
could not be converted into
|
||||
'{| p: nil |}'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_top_properties")
|
||||
|
@ -781,59 +698,35 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_top_properties")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'{ p: number?, q: any } & { p: unknown, q: string? }'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'{ p: string?, q: number? }'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `string?`, and `number` is not exactly `string?`\n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` results in `number?` and accessing `p` has the 1st "
|
||||
"component of the union as `string`, and `number?` is not exactly `string`\n\t"
|
||||
" * in the 1st component of the intersection, accessing `q` results in `any` and accessing `q` results in "
|
||||
"`number?`, and `any` is not exactly `number?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `p` results in `unknown` and accessing `p` results in "
|
||||
"`string?`, and `unknown` is not exactly `string?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `q` has the 1st component of the union as `string` and "
|
||||
"accessing `q` results in `number?`, and `string` is not exactly `number?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `q` results in `string?` and accessing `q` has the 1st "
|
||||
"component of the union as `number`, and `string?` is not exactly `number`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'{ p: number?, q: any } & { p: unknown, q: string? }'
|
||||
could not be converted into
|
||||
'{ p: string?, q: number? }'; type { p: number?, q: any } & { p: unknown, q: string? }[0][read "p"] (number?) is not exactly { p: string?, q: number? }[read "p"][0] (string)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[0][read "p"][0] (number) is not exactly { p: string?, q: number? }[read "p"] (string?)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[0][read "q"] (any) is not exactly { p: string?, q: number? }[read "q"] (number?)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[1][read "p"] (unknown) is not exactly { p: string?, q: number? }[read "p"] (string?)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[1][read "q"] (string?) is not exactly { p: string?, q: number? }[read "q"][0] (number)
|
||||
type { p: number?, q: any } & { p: unknown, q: string? }[1][read "q"][0] (string) is not exactly { p: string?, q: number? }[read "q"] (number?))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'{| p: number?, q: any |} & {| p: unknown, q: string? |}'
|
||||
could not be converted into
|
||||
'{| p: string?, q: number? |}'; none of the intersection parts are compatible)";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'{ p: number?, q: any } & { p: unknown, q: string? }'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'{ p: string?, q: number? }'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` has the 1st component of the union as `number` and "
|
||||
"accessing `p` results in `string?`, and `number` is not exactly `string?`\n\t"
|
||||
" * in the 1st component of the intersection, accessing `p` results in `number?` and accessing `p` has the 1st "
|
||||
"component of the union as `string`, and `number?` is not exactly `string`\n\t"
|
||||
" * in the 1st component of the intersection, accessing `q` results in `any` and accessing `q` results in "
|
||||
"`number?`, and `any` is not exactly `number?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `p` results in `unknown` and accessing `p` results in "
|
||||
"`string?`, and `unknown` is not exactly `string?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `q` has the 1st component of the union as `string` and "
|
||||
"accessing `q` results in `number?`, and `string` is not exactly `number?`\n\t"
|
||||
" * in the 2nd component of the intersection, accessing `q` results in `string?` and accessing `q` has the 1st "
|
||||
"component of the union as `number`, and `string?` is not exactly `number`";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'{| p: number?, q: any |} & {| p: unknown, q: string? |}'
|
||||
'{| p: number?, q: any |} & {| p: unknown, q: string? |}'
|
||||
could not be converted into
|
||||
'{| p: string?, q: number? |}'; none of the intersection parts are compatible)";
|
||||
'{| p: string?, q: number? |}'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
@ -859,7 +752,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_returning_intersections")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected1 =
|
||||
"Type\n\t"
|
||||
|
@ -904,32 +797,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_returning_intersections")
|
|||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })'
|
||||
could not be converted into
|
||||
'(nil) -> { p: number, q: number, r: number }'; type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][0] ({ p: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][1] ({ q: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][0] ({ p: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][1] ({ r: number }) is not a subtype of (nil) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })'
|
||||
could not be converted into
|
||||
'(number?) -> { p: number, q: number, r: number }'; type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][0] ({ p: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[0].returns()[0][1] ({ q: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].arguments()[0] (string?) is not a supertype of (number?) -> { p: number, q: number, r: number }.arguments()[0][0] (number)
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][0] ({ p: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number })
|
||||
type ((number?) -> { p: number } & { q: number }) & ((string?) -> { p: number } & { r: number })[1].returns()[0][1] ({ r: number }) is not a subtype of (number?) -> { p: number, q: number, r: number }.returns()[0] ({ p: number, q: number, r: number }))",
|
||||
toString(result.errors[1])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(
|
||||
|
@ -940,17 +808,6 @@ could not be converted into
|
|||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?) -> {| p: number |} & {| q: number |}) & ((string?) -> {| p: number |} & {| r: number |})'
|
||||
could not be converted into
|
||||
'(number?) -> {| p: number, q: number, r: number |}'; none of the intersection parts are compatible)",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic")
|
||||
|
@ -967,7 +824,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic")
|
|||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(0, result);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
|
@ -976,15 +833,6 @@ could not be converted into
|
|||
'(number?) -> a'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((number?) -> a | number) & ((string?) -> a | string)'
|
||||
could not be converted into
|
||||
'(number?) -> a'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generics")
|
||||
|
@ -1003,7 +851,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generics")
|
|||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
|
@ -1012,15 +860,6 @@ could not be converted into
|
|||
'(a?) -> (a & c) | b'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((a?) -> a | b) & ((c?) -> b | c)'
|
||||
could not be converted into
|
||||
'(a?) -> (a & c) | b'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic_packs")
|
||||
|
@ -1034,7 +873,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic_packs")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected1 =
|
||||
"Type\n\t"
|
||||
|
@ -1061,27 +900,7 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_functions_mentioning_generic_packs")
|
|||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))'
|
||||
could not be converted into
|
||||
'(nil, a...) -> (nil, b...)'; type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[0].returns()[0][0] (number) is not a subtype of (nil, a...) -> (nil, b...).returns()[0] (nil)
|
||||
type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[1].returns()[0][0] (string) is not a subtype of (nil, a...) -> (nil, b...).returns()[0] (nil))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))'
|
||||
could not be converted into
|
||||
'(nil, b...) -> (nil, a...)'; type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[0].returns()[0][0] (number) is not a subtype of (nil, b...) -> (nil, a...).returns()[0] (nil)
|
||||
type ((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))[1].returns()[0][0] (string) is not a subtype of (nil, b...) -> (nil, a...).returns()[0] (nil))",
|
||||
toString(result.errors[1])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
|
@ -1090,15 +909,6 @@ could not be converted into
|
|||
'(nil, b...) -> (nil, a...)'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((number?, a...) -> (number?, b...)) & ((string?, a...) -> (string?, b...))'
|
||||
could not be converted into
|
||||
'(nil, b...) -> (nil, a...)'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_result")
|
||||
|
@ -1117,22 +927,11 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_result")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((nil) -> unknown) & ((number) -> number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number?) -> number?'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'((nil) -> unknown) & ((number) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> number?'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((nil) -> unknown) & ((number) -> number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number?) -> number?'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_arguments")
|
||||
|
@ -1151,22 +950,12 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_unknown_arguments")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((number) -> number?) & ((unknown) -> string?)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number?) -> nil'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'((number) -> number?) & ((unknown) -> string?)'
|
||||
could not be converted into
|
||||
'(number?) -> nil'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'((number) -> number?) & ((unknown) -> string?)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number?) -> nil'; none of the intersection parts are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_result")
|
||||
|
@ -1180,7 +969,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_result")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected1 =
|
||||
"Type\n\t"
|
||||
|
@ -1209,28 +998,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_result")
|
|||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((nil) -> never) & ((number) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> number'; type ((nil) -> never) & ((number) -> number)[0].arguments()[0] (number) is not a supertype of (number?) -> number.arguments()[0][1] (nil)
|
||||
type ((nil) -> never) & ((number) -> number)[1].arguments()[0] (nil) is not a supertype of (number?) -> number.arguments()[0][0] (number))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((nil) -> never) & ((number) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> never'; type ((nil) -> never) & ((number) -> number)[0].arguments()[0] (number) is not a supertype of (number?) -> never.arguments()[0][1] (nil)
|
||||
type ((nil) -> never) & ((number) -> number)[0].returns()[0] (number) is not a subtype of (number?) -> never.returns()[0] (never)
|
||||
type ((nil) -> never) & ((number) -> number)[1].arguments()[0] (nil) is not a supertype of (number?) -> never.arguments()[0][0] (number))",
|
||||
toString(result.errors[1])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
|
@ -1239,15 +1007,6 @@ could not be converted into
|
|||
'(number?) -> never'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((nil) -> never) & ((number) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> never'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_arguments")
|
||||
|
@ -1261,7 +1020,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_arguments")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected1 =
|
||||
"Type\n\t"
|
||||
|
@ -1294,26 +1053,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_never_arguments")
|
|||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
const std::string expected1 = R"(Type
|
||||
'((never) -> string?) & ((number) -> number?)'
|
||||
could not be converted into
|
||||
'(never) -> nil'; type ((never) -> string?) & ((number) -> number?)[0].returns()[0][0] (number) is not a subtype of (never) -> nil.returns()[0] (nil)
|
||||
type ((never) -> string?) & ((number) -> number?)[1].returns()[0][0] (string) is not a subtype of (never) -> nil.returns()[0] (nil))";
|
||||
const std::string expected2 = R"(Type
|
||||
'((never) -> string?) & ((number) -> number?)'
|
||||
could not be converted into
|
||||
'(number?) -> nil'; type ((never) -> string?) & ((number) -> number?)[0].arguments()[0] (number) is not a supertype of (number?) -> nil.arguments()[0][1] (nil)
|
||||
type ((never) -> string?) & ((number) -> number?)[0].returns()[0][0] (number) is not a subtype of (number?) -> nil.returns()[0] (nil)
|
||||
type ((never) -> string?) & ((number) -> number?)[1].arguments()[0] (never) is not a supertype of (number?) -> nil.arguments()[0][0] (number)
|
||||
type ((never) -> string?) & ((number) -> number?)[1].arguments()[0] (never) is not a supertype of (number?) -> nil.arguments()[0][1] (nil)
|
||||
type ((never) -> string?) & ((number) -> number?)[1].returns()[0][0] (string) is not a subtype of (number?) -> nil.returns()[0] (nil))";
|
||||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
|
@ -1322,15 +1062,6 @@ could not be converted into
|
|||
'(number?) -> nil'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'((never) -> string?) & ((number) -> number?)'
|
||||
could not be converted into
|
||||
'(number?) -> nil'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_overlapping_results_and_variadics")
|
||||
|
@ -1347,22 +1078,11 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_overlapping_results_and_
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((number?) -> (...number)) & ((string?) -> number | string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number | string) -> (number, number?)'; none of the intersection parts are compatible";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'((number?) -> (...number)) & ((string?) -> number | string)'
|
||||
could not be converted into
|
||||
'(number | string) -> (number, number?)'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((number?) -> (...number)) & ((string?) -> number | string)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(number | string) -> (number, number?)'; none of the intersection parts are compatible";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_1")
|
||||
|
@ -1430,7 +1150,7 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_3")
|
|||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
|
@ -1439,15 +1159,6 @@ could not be converted into
|
|||
'() -> number'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type
|
||||
'(() -> (a...)) & (() -> (number?, a...))'
|
||||
could not be converted into
|
||||
'() -> number'; none of the intersection parts are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_4")
|
||||
|
@ -1463,31 +1174,21 @@ TEST_CASE_FIXTURE(Fixture, "overloadeded_functions_with_weird_typepacks_4")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'((a...) -> ()) & ((number, a...) -> number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((a...) -> ()) & ((number, a...) -> number)'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, the function returns is `()` in the former type and `number` in "
|
||||
"the latter type, and `()` is not a subtype of `number`\n\t"
|
||||
" * in the 2nd component of the intersection, the function takes a tail of `a...` and in the 1st component of "
|
||||
"the intersection, the function takes a tail of `a...`, and `a...` is not a supertype of `a...`";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'((a...) -> ()) & ((number, a...) -> number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((a...) -> ()) & ((number, a...) -> number)'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st component of the intersection, the function returns is `()` in the former type and `number` in "
|
||||
"the latter type, and `()` is not a subtype of `number`\n\t"
|
||||
" * in the 2nd component of the intersection, the function takes a tail of `a...` and in the 1st component of "
|
||||
"the intersection, the function takes a tail of `a...`, and `a...` is not a supertype of `a...`";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((a...) -> ()) & ((number, a...) -> number)'
|
||||
could not be converted into
|
||||
'((a...) -> ()) & ((number, a...) -> number)'; at [0].returns(), is not a subtype of number
|
||||
type ((a...) -> ()) & ((number, a...) -> number)[1].arguments().tail() (a...) is not a supertype of ((a...) -> ()) & ((number, a...) -> number)[0].arguments().tail() (a...))",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
|
@ -1497,16 +1198,6 @@ could not be converted into
|
|||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(
|
||||
R"(Type
|
||||
'((a...) -> ()) & ((number, a...) -> number)'
|
||||
could not be converted into
|
||||
'(number?) -> ()'; none of the intersection parts are compatible)",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "intersect_metatables")
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
|
@ -465,20 +464,14 @@ local b: B.T = a
|
|||
CheckResult result = frontend.check("game/C");
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; \n"
|
||||
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||
"`number` is not exactly `string`";
|
||||
const std::string expected =
|
||||
"Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; \n"
|
||||
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||
"`number` is not exactly `string`";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(
|
||||
toString(result.errors.at(0)) ==
|
||||
"Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'; at [read \"x\"], number is not exactly string"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'T' from 'game/A' could not be converted into 'T' from 'game/B'
|
||||
|
@ -518,20 +511,14 @@ local b: B.T = a
|
|||
CheckResult result = frontend.check("game/D");
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; \n"
|
||||
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||
"`number` is not exactly `string`";
|
||||
const std::string expected =
|
||||
"Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; \n"
|
||||
"this is because accessing `x` results in `number` in the former type and `string` in the latter type, and "
|
||||
"`number` is not exactly `string`";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(
|
||||
toString(result.errors.at(0)) ==
|
||||
"Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'; at [read \"x\"], number is not exactly string"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'T' from 'game/B' could not be converted into 'T' from 'game/C'
|
||||
|
|
|
@ -19,7 +19,6 @@ LUAU_FASTINT(LuauTarjanChildLimit)
|
|||
LUAU_FASTINT(LuauTypeInferIterationLimit)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
|
||||
TEST_SUITE_BEGIN("ProvisionalTests");
|
||||
|
||||
|
@ -874,20 +873,13 @@ TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_ty
|
|||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = (FFlag::LuauImproveTypePathsInErrors) ?
|
||||
R"(Type
|
||||
const std::string expected =
|
||||
R"(Type
|
||||
'{| x: number? |}'
|
||||
could not be converted into
|
||||
'{| x: number |}'
|
||||
caused by:
|
||||
Property 'x' is not compatible.
|
||||
Type 'number?' could not be converted into 'number' in an invariant context)"
|
||||
: R"(Type
|
||||
'{| x: number? |}'
|
||||
could not be converted into
|
||||
'{| x: number |}'
|
||||
caused by:
|
||||
Property 'x' is not compatible.
|
||||
Type 'number?' could not be converted into 'number' in an invariant context)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(DebugLuauEqSatSimplification)
|
||||
LUAU_FASTFLAG(LuauIntersectNotNil)
|
||||
LUAU_FASTFLAG(LuauSkipNoRefineDuringRefinement)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauFunctionCallsAreNotNilable)
|
||||
LUAU_FASTFLAG(LuauSimplyRefineNotNil)
|
||||
LUAU_FASTFLAG(LuauWeakNilRefinementType)
|
||||
LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions)
|
||||
LUAU_FASTFLAG(LuauSimplificationTableExternType)
|
||||
LUAU_FASTFLAG(LuauAvoidDoubleNegation)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -1243,6 +1243,8 @@ TEST_CASE_FIXTURE(Fixture, "apply_refinements_on_astexprindexexpr_whose_subscrip
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauAvoidDoubleNegation, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type T = {tag: "missing", x: nil} | {tag: "exists", x: string}
|
||||
|
||||
|
@ -1259,9 +1261,12 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
// CLI-115281 Types produced by refinements do not consistently get simplified
|
||||
// CLI-115281 Types produced by refinements do not consistently get
|
||||
// simplified. Sometimes this is due to not refining at the correct
|
||||
// time, sometimes this is due to hitting the simplifier rather than
|
||||
// normalization.
|
||||
CHECK("{ tag: \"exists\", x: string } & { x: ~(false?) }" == toString(requireTypeAtPosition({5, 28})));
|
||||
CHECK("({ tag: \"exists\", x: string } & { x: ~~(false?) }) | { tag: \"missing\", x: nil }" == toString(requireTypeAtPosition({7, 28})));
|
||||
CHECK(R"(({ tag: "exists", x: string } & { x: false? }) | ({ tag: "missing", x: nil } & { x: false? }))" == toString(requireTypeAtPosition({7, 28})));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2552,8 +2557,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "truthy_refinement_on_generic")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "truthy_call_of_function_with_table_value_as_argument_should_not_refine_value_as_never")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauSkipNoRefineDuringRefinement, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Item = {}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ using namespace Luau;
|
|||
|
||||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
LUAU_FASTFLAG(LuauPropagateExpectedTypesForCalls)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
|
||||
TEST_SUITE_BEGIN("TypeSingletons");
|
||||
|
||||
|
@ -387,7 +386,7 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'{ [\"\\n\"]: number }'"
|
||||
|
@ -395,13 +394,6 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
|
|||
"'{ [\"<>\"]: number }'";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK(
|
||||
"Type\n"
|
||||
" '{ [\"\\n\"]: number }'\n"
|
||||
"could not be converted into\n"
|
||||
" '{ [\"<>\"]: number }'" == toString(result.errors[0])
|
||||
);
|
||||
else
|
||||
CHECK_EQ(
|
||||
R"(Table type '{ ["\n"]: number }' not compatible with type '{| ["<>"]: number |}' because the former is missing field '<>')",
|
||||
|
@ -471,23 +463,12 @@ TEST_CASE_FIXTURE(Fixture, "parametric_tagged_union_alias")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expectedError = "Type\n\t"
|
||||
"'{ result: string, success: boolean }'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'Err<number> | Ok<string>'";
|
||||
CHECK(toString(result.errors[0]) == expectedError);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expectedError = R"(Type
|
||||
'{ result: string, success: boolean }'
|
||||
could not be converted into
|
||||
'Err<number> | Ok<string>')";
|
||||
const std::string expectedError = "Type\n\t"
|
||||
"'{ result: string, success: boolean }'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'Err<number> | Ok<string>'";
|
||||
CHECK(toString(result.errors[0]) == expectedError);
|
||||
|
||||
CHECK(toString(result.errors[0]) == expectedError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "if_then_else_expression_singleton_options")
|
||||
|
|
|
@ -24,12 +24,10 @@ LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering)
|
|||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAG(DebugLuauAssertOnForcedConstraint)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauBidirectionalInferenceCollectIndexerTypes)
|
||||
LUAU_FASTFLAG(LuauBidirectionalFailsafe)
|
||||
LUAU_FASTFLAG(LuauBidirectionalInferenceElideAssert)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck)
|
||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||
|
||||
TEST_SUITE_BEGIN("TableTests");
|
||||
|
||||
|
@ -924,7 +922,7 @@ TEST_CASE_FIXTURE(Fixture, "sealed_table_indexers_must_unify")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(
|
||||
"Type pack '{number}' could not be converted into '{string}'; \n"
|
||||
|
@ -933,14 +931,6 @@ TEST_CASE_FIXTURE(Fixture, "sealed_table_indexers_must_unify")
|
|||
"and `number` is not exactly `string`" == toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
// CLI-114879 - Error path reporting is not great
|
||||
CHECK(
|
||||
toString(result.errors[0]) ==
|
||||
"Type pack '{number}' could not be converted into '{string}'; at [0].indexResult(), number is not exactly string"
|
||||
);
|
||||
}
|
||||
else
|
||||
CHECK_MESSAGE(nullptr != get<TypeMismatch>(result.errors[0]), "Expected a TypeMismatch but got " << result.errors[0]);
|
||||
}
|
||||
|
@ -1816,7 +1806,7 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multi
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK_EQ(
|
||||
"Type pack '{ x: number }' could not be converted into '{ x: number, y: number, z: number }'; \n"
|
||||
|
@ -1825,14 +1815,6 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_with_missing_props_dont_report_multi
|
|||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK_EQ(
|
||||
"Type pack '{ x: number }' could not be converted into '{ x: number, y: number, z: number }';"
|
||||
" at [0], { x: number } is not a subtype of { x: number, y: number, z: number }",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
MissingProperties* mp = get<MissingProperties>(result.errors[0]);
|
||||
|
@ -2455,7 +2437,7 @@ local b: B = a
|
|||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(
|
||||
"Type 'A' could not be converted into 'B'; \n"
|
||||
|
@ -2463,8 +2445,6 @@ local b: B = a
|
|||
"`string`" == toString(result.errors.at(0))
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK(toString(result.errors.at(0)) == R"(Type 'A' could not be converted into 'B'; at [read "y"], number is not exactly string)");
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'A' could not be converted into 'B'
|
||||
|
@ -2490,7 +2470,7 @@ local b: B = a
|
|||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(
|
||||
"Type 'A' could not be converted into 'B'; \n"
|
||||
|
@ -2498,8 +2478,6 @@ local b: B = a
|
|||
"`string`" == toString(result.errors.at(0))
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK(toString(result.errors.at(0)) == R"(Type 'A' could not be converted into 'B'; at [read "b"][read "y"], number is not exactly string)");
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'A' could not be converted into 'B'
|
||||
|
@ -2525,8 +2503,8 @@ local b2 = setmetatable({ x = 2, y = 4 }, { __call = function(s, t) end });
|
|||
local c2: typeof(a2) = b2
|
||||
)");
|
||||
|
||||
const std::string expected1 = (FFlag::LuauImproveTypePathsInErrors) ?
|
||||
R"(Type 'b1' could not be converted into 'a1'
|
||||
const std::string expected1 =
|
||||
R"(Type 'b1' could not be converted into 'a1'
|
||||
caused by:
|
||||
Type
|
||||
'{ x: number, y: string }'
|
||||
|
@ -2534,18 +2512,9 @@ could not be converted into
|
|||
'{ x: number, y: number }'
|
||||
caused by:
|
||||
Property 'y' is not compatible.
|
||||
Type 'string' could not be converted into 'number' in an invariant context)"
|
||||
: R"(Type 'b1' could not be converted into 'a1'
|
||||
caused by:
|
||||
Type
|
||||
'{ x: number, y: string }'
|
||||
could not be converted into
|
||||
'{ x: number, y: number }'
|
||||
caused by:
|
||||
Property 'y' is not compatible.
|
||||
Type 'string' could not be converted into 'number' in an invariant context)";
|
||||
const std::string expected2 = (FFlag::LuauImproveTypePathsInErrors) ?
|
||||
R"(Type 'b2' could not be converted into 'a2'
|
||||
const std::string expected2 =
|
||||
R"(Type 'b2' could not be converted into 'a2'
|
||||
caused by:
|
||||
Type
|
||||
'{ __call: <a, b>(a, b) -> () }'
|
||||
|
@ -2556,21 +2525,9 @@ caused by:
|
|||
Type
|
||||
'<a, b>(a, b) -> ()'
|
||||
could not be converted into
|
||||
'<a>(a) -> ()'; different number of generic type parameters)"
|
||||
: R"(Type 'b2' could not be converted into 'a2'
|
||||
caused by:
|
||||
Type
|
||||
'{ __call: <a, b>(a, b) -> () }'
|
||||
could not be converted into
|
||||
'{ __call: <a>(a) -> () }'
|
||||
caused by:
|
||||
Property '__call' is not compatible.
|
||||
Type
|
||||
'<a, b>(a, b) -> ()'
|
||||
could not be converted into
|
||||
'<a>(a) -> ()'; different number of generic type parameters)";
|
||||
'<a>(a) -> ()'; different number of generic type parameters)";
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
// The assignment of c2 to b2 is, surprisingly, allowed under the new
|
||||
// solver for two reasons:
|
||||
|
@ -2586,18 +2543,6 @@ could not be converted into
|
|||
"`string` is not exactly `number`" == toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
// The assignment of c2 to b2 is, surprisingly, allowed under the new
|
||||
// solver for two reasons:
|
||||
//
|
||||
// First, both of the __call functions have hidden ...any arguments
|
||||
// because their exact definition is available.
|
||||
//
|
||||
// Second, nil <: unknown, so we consider that parameter to be optional.
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK("Type 'b1' could not be converted into 'a1'; at table()[read \"y\"], string is not exactly number" == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauInstantiateInSubtyping)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
|
@ -2606,15 +2551,15 @@ could not be converted into
|
|||
const std::string expected3 = R"(Type 'b2' could not be converted into 'a2'
|
||||
caused by:
|
||||
Type
|
||||
'{ __call: <a, b>(a, b) -> () }'
|
||||
'{ __call: <a, b>(a, b) -> () }'
|
||||
could not be converted into
|
||||
'{ __call: <a>(a) -> () }'
|
||||
'{ __call: <a>(a) -> () }'
|
||||
caused by:
|
||||
Property '__call' is not compatible.
|
||||
Type
|
||||
'<a, b>(a, b) -> ()'
|
||||
'<a, b>(a, b) -> ()'
|
||||
could not be converted into
|
||||
'<a>(a) -> ()'; different number of generic type parameters)";
|
||||
'<a>(a) -> ()'; different number of generic type parameters)";
|
||||
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
}
|
||||
|
@ -2626,15 +2571,15 @@ could not be converted into
|
|||
std::string expected3 = R"(Type 'b2' could not be converted into 'a2'
|
||||
caused by:
|
||||
Type
|
||||
'{ __call: (a, b) -> () }'
|
||||
'{ __call: (a, b) -> () }'
|
||||
could not be converted into
|
||||
'{ __call: <a>(a) -> () }'
|
||||
'{ __call: <a>(a) -> () }'
|
||||
caused by:
|
||||
Property '__call' is not compatible.
|
||||
Type
|
||||
'(a, b) -> ()'
|
||||
'(a, b) -> ()'
|
||||
could not be converted into
|
||||
'<a>(a) -> ()'; different number of generic type parameters)";
|
||||
'<a>(a) -> ()'; different number of generic type parameters)";
|
||||
CHECK_EQ(expected3, toString(result.errors[1]));
|
||||
}
|
||||
}
|
||||
|
@ -2651,7 +2596,7 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_key")
|
|||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(
|
||||
"Type 'A' could not be converted into 'B'; \n"
|
||||
|
@ -2659,10 +2604,6 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_key")
|
|||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK("Type 'A' could not be converted into 'B'; at indexer(), number is not exactly string" == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'A' could not be converted into 'B'
|
||||
|
@ -2685,7 +2626,7 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_value")
|
|||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(
|
||||
"Type 'A' could not be converted into 'B'; \n"
|
||||
|
@ -2693,10 +2634,6 @@ TEST_CASE_FIXTURE(Fixture, "error_detailed_indexer_value")
|
|||
"`string`" == toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK("Type 'A' could not be converted into 'B'; at indexResult(), number is not exactly string" == toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'A' could not be converted into 'B'
|
||||
|
@ -2742,17 +2679,12 @@ local y: number = tmp.p.y
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
CHECK(
|
||||
"Type 'tmp' could not be converted into 'HasSuper'; \n"
|
||||
"this is because accessing `p` results in `{ x: number, y: number }` in the former type and `Super` in the latter type, and `{ x: "
|
||||
"number, y: number }` is not exactly `Super`" == toString(result.errors[0])
|
||||
);
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK(
|
||||
"Type 'tmp' could not be converted into 'HasSuper'; at [read \"p\"], { x: number, y: number } is not exactly Super" ==
|
||||
toString(result.errors[0])
|
||||
);
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type 'tmp' could not be converted into 'HasSuper'
|
||||
|
@ -3712,21 +3644,13 @@ TEST_CASE_FIXTURE(Fixture, "mixed_tables_with_implicit_numbered_keys")
|
|||
local t: { [string]: number } = { 5, 6, 7 }
|
||||
)");
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
std::string expected =
|
||||
"Type '{number}' could not be converted into '{ [string]: number }'; \n"
|
||||
"this is because the index type is `number` in the former type and `string` in the latter type, and `number` is not exactly `string`";
|
||||
CHECK(toString(result.errors[0]) == expected);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
CHECK(
|
||||
"Type '{number}' could not be converted into '{ [string]: number }'; at indexer(), number is not exactly string" ==
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
|
@ -3824,6 +3748,8 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shap
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauNonReentrantGeneralization2, true}, {FFlag::LuauReportSubtypingErrors, true}};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(s)
|
||||
return s:absolutely_no_scalar_has_this_method()
|
||||
|
@ -3861,7 +3787,7 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_
|
|||
CHECK("typeof(string)" == toString(tm4->givenType));
|
||||
CHECK("t1 where t1 = { read absolutely_no_scalar_has_this_method: (t1) -> (a...) }" == toString(tm4->wantedType));
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
|
||||
|
@ -3886,36 +3812,6 @@ could not be converted into
|
|||
caused by:
|
||||
Not all union options are compatible.
|
||||
Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
|
||||
caused by:
|
||||
The former's metatable does not satisfy the requirements.
|
||||
Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')";
|
||||
CHECK_EQ(expected3, toString(result.errors[2]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||
|
||||
const std::string expected1 =
|
||||
R"(Type 'string' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
|
||||
caused by:
|
||||
The former's metatable does not satisfy the requirements.
|
||||
Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')";
|
||||
CHECK_EQ(expected1, toString(result.errors[0]));
|
||||
|
||||
const std::string expected2 =
|
||||
R"(Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
|
||||
caused by:
|
||||
The former's metatable does not satisfy the requirements.
|
||||
Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')";
|
||||
CHECK_EQ(expected2, toString(result.errors[1]));
|
||||
|
||||
const std::string expected3 = R"(Type
|
||||
'"bar" | "baz"'
|
||||
could not be converted into
|
||||
't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
|
||||
caused by:
|
||||
Not all union options are compatible.
|
||||
Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}'
|
||||
caused by:
|
||||
The former's metatable does not satisfy the requirements.
|
||||
Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')";
|
||||
|
@ -4467,25 +4363,13 @@ TEST_CASE_FIXTURE(Fixture, "identify_all_problematic_table_fields")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
std::string expected =
|
||||
"Type '{ a: string, b: boolean, c: number }' could not be converted into 'T'; \n"
|
||||
"this is because \n\t"
|
||||
" * accessing `a` results in `string` in the former type and `number` in the latter type, and `string` is not exactly `number`\n\t"
|
||||
" * accessing `b` results in `boolean` in the former type and `string` in the latter type, and `boolean` is not exactly `string`\n\t"
|
||||
" * accessing `c` results in `number` in the former type and `boolean` in the latter type, and `number` is not exactly `boolean`";
|
||||
CHECK(toString(result.errors[0]) == expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string expected =
|
||||
"Type '{ a: string, b: boolean, c: number }' could not be converted into 'T'; at [read \"a\"], string is not exactly number"
|
||||
"\n\tat [read \"b\"], boolean is not exactly string"
|
||||
"\n\tat [read \"c\"], number is not exactly boolean";
|
||||
CHECK(toString(result.errors[0]) == expected);
|
||||
}
|
||||
std::string expected =
|
||||
"Type '{ a: string, b: boolean, c: number }' could not be converted into 'T'; \n"
|
||||
"this is because \n\t"
|
||||
" * accessing `a` results in `string` in the former type and `number` in the latter type, and `string` is not exactly `number`\n\t"
|
||||
" * accessing `b` results in `boolean` in the former type and `string` in the latter type, and `boolean` is not exactly `string`\n\t"
|
||||
" * accessing `c` results in `number` in the former type and `boolean` in the latter type, and `number` is not exactly `boolean`";
|
||||
CHECK(toString(result.errors[0]) == expected);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "read_and_write_only_table_properties_are_unsupported")
|
||||
|
@ -5125,29 +5009,17 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "subtyping_with_a_metatable_table_path")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
CHECK_EQ(
|
||||
"Type pack '{ @metatable { }, { } & { } }' could not be converted into 'Class'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st entry in the type pack, the metatable portion is `{ }` in the former type and `nil` in the latter type, and `{ }` "
|
||||
"is not a subtype of `nil`\n\t"
|
||||
" * in the 1st entry in the type pack, the table portion has the 1st component of the intersection as `{ }` and in the 1st entry "
|
||||
"in the type pack, the table portion is `nil`, and `{ }` is not a subtype of `nil`\n\t"
|
||||
" * in the 1st entry in the type pack, the table portion has the 2nd component of the intersection as `{ }` and in the 1st entry "
|
||||
"in the type pack, the table portion is `nil`, and `{ }` is not a subtype of `nil`",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(
|
||||
"Type pack '{ @metatable { }, { } & { } }' could not be converted into 'Class'; at [0].metatable(), { } is not a subtype of nil\n"
|
||||
"\ttype { @metatable { }, { } & { } }[0].table()[0] ({ }) is not a subtype of Class[0].table() (nil)\n"
|
||||
"\ttype { @metatable { }, { } & { } }[0].table()[1] ({ }) is not a subtype of Class[0].table() (nil)",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
CHECK_EQ(
|
||||
"Type pack '{ @metatable { }, { } & { } }' could not be converted into 'Class'; \n"
|
||||
"this is because \n\t"
|
||||
" * in the 1st entry in the type pack, the metatable portion is `{ }` in the former type and `nil` in the latter type, and `{ }` "
|
||||
"is not a subtype of `nil`\n\t"
|
||||
" * in the 1st entry in the type pack, the table portion has the 1st component of the intersection as `{ }` and in the 1st entry "
|
||||
"in the type pack, the table portion is `nil`, and `{ }` is not a subtype of `nil`\n\t"
|
||||
" * in the 1st entry in the type pack, the table portion has the 2nd component of the intersection as `{ }` and in the 1st entry "
|
||||
"in the type pack, the table portion is `nil`, and `{ }` is not a subtype of `nil`",
|
||||
toString(result.errors[0])
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_union_type")
|
||||
|
@ -5178,10 +5050,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_union_type")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_check_constraint_too_eager")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
||||
};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function doTheThing(_: { [string]: unknown }) end
|
||||
|
@ -5221,10 +5090,7 @@ TEST_CASE_FIXTURE(Fixture, "function_check_constraint_too_eager")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "magic_functions_bidirectionally_inferred")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
||||
};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function getStuff(): (string, number, string)
|
||||
|
@ -5484,10 +5350,7 @@ TEST_CASE_FIXTURE(Fixture, "oss_1543_optional_generic_param")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
||||
};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
auto result = check(R"(
|
||||
type Book = { title: string, author: string }
|
||||
|
@ -5515,10 +5378,7 @@ TEST_CASE_FIXTURE(Fixture, "missing_fields_bidirectional_inference")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "generic_index_syntax_bidirectional_infer_with_tables")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
||||
};
|
||||
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||
|
||||
auto result = check((R"(
|
||||
local function getStatus(): string
|
||||
|
@ -5572,7 +5432,6 @@ TEST_CASE_FIXTURE(Fixture, "bigger_nested_table_causes_big_type_error")
|
|||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauImproveTypePathsInErrors, true},
|
||||
};
|
||||
|
||||
auto result = check(R"(
|
||||
|
@ -5606,22 +5465,22 @@ TEST_CASE_FIXTURE(Fixture, "bigger_nested_table_causes_big_type_error")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
std::string expected = "Type\n\t"
|
||||
"'{Dir | File | { children: ({Dir | File | { content: string?, path: string, type: \"file\" }} | {Dir | File})?, name: "
|
||||
"string, type: \"dir\" }}'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'DirectoryChildren'; \n"
|
||||
"this is because in the result of indexing has the 3rd component of the union as `{ children: ({Dir | File | { content: "
|
||||
"string?, path: string, type: \"file\" }} | {Dir | File})?, name: string, type: \"dir\" }` and the result of indexing is "
|
||||
"`Dir | File`, and `{ children: ({Dir | File | { content: string?, path: string, type: \"file\" }} | {Dir | File})?, "
|
||||
"name: string, type: \"dir\" }` is not exactly `Dir | File`";
|
||||
std::string expected =
|
||||
"Type\n\t"
|
||||
"'{Dir | File | { children: ({Dir | File | { content: string?, path: string, type: \"file\" }} | {Dir | File})?, name: "
|
||||
"string, type: \"dir\" }}'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'DirectoryChildren'; \n"
|
||||
"this is because in the result of indexing has the 3rd component of the union as `{ children: ({Dir | File | { content: "
|
||||
"string?, path: string, type: \"file\" }} | {Dir | File})?, name: string, type: \"dir\" }` and the result of indexing is "
|
||||
"`Dir | File`, and `{ children: ({Dir | File | { content: string?, path: string, type: \"file\" }} | {Dir | File})?, "
|
||||
"name: string, type: \"dir\" }` is not exactly `Dir | File`";
|
||||
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "unsafe_bidirectional_mutation")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauBidirectionalFailsafe, true};
|
||||
// It's kind of suspect that we allow multiple definitions of keys in
|
||||
// a single table.
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
|
@ -5641,7 +5500,6 @@ TEST_CASE_FIXTURE(Fixture, "unsafe_bidirectional_mutation")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "function_call_in_indexer_with_compound_assign")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauBidirectionalFailsafe, true};
|
||||
// This has a bunch of errors, we really just need it to not crash / assert.
|
||||
std::ignore = check(R"(
|
||||
--!strict
|
||||
|
@ -5676,10 +5534,8 @@ TEST_CASE_FIXTURE(Fixture, "stop_refining_new_table_indices_for_non_primitive_ta
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "fuzz_match_literal_type_crash_again")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true},
|
||||
{FFlag::LuauBidirectionalInferenceElideAssert, true},
|
||||
};
|
||||
ScopedFastFlag _{FFlag::LuauBidirectionalInferenceElideAssert, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function f(_: { [string]: {unknown}} ) end
|
||||
f(
|
||||
|
@ -5694,8 +5550,6 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_match_literal_type_crash_again")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "type_mismatch_in_dict")
|
||||
{
|
||||
ScopedFastFlag sff{FFlag::LuauBidirectionalInferenceCollectIndexerTypes, true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
--!strict
|
||||
local dict: {[string]: boolean} = {
|
||||
|
|
|
@ -24,20 +24,19 @@ LUAU_FASTINT(LuauNormalizeCacheLimit)
|
|||
LUAU_FASTINT(LuauRecursionLimit)
|
||||
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit)
|
||||
LUAU_FASTINT(LuauTypeInferRecursionLimit)
|
||||
LUAU_FASTFLAG(LuauAstTypeGroup3)
|
||||
LUAU_FASTFLAG(LuauNewNonStrictWarnOnUnknownGlobals)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(LuauTypeCheckerAcceptNumberConcats)
|
||||
LUAU_FASTFLAG(LuauPreprocessTypestatedArgument)
|
||||
LUAU_FASTFLAG(LuauCacheInferencePerAstExpr)
|
||||
LUAU_FASTFLAG(LuauLimitIterationWhenCheckingArgumentCounts)
|
||||
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked)
|
||||
LUAU_FASTFLAG(LuauMagicFreezeCheckBlocked2)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
LUAU_FASTFLAG(LuauOptimizeFalsyAndTruthyIntersect)
|
||||
LUAU_FASTFLAG(LuauHasPropProperBlock)
|
||||
LUAU_FASTFLAG(LuauStringPartLengthLimit)
|
||||
LUAU_FASTFLAG(LuauSimplificationRecheckAssumption)
|
||||
LUAU_FASTFLAG(LuauAlwaysResolveAstTypes)
|
||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||
LUAU_FASTFLAG(LuauAvoidDoubleNegation)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -1144,8 +1143,12 @@ TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
|
|||
end
|
||||
)");
|
||||
|
||||
if (FFlag::LuauInstantiateInSubtyping && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauInstantiateInSubtyping)
|
||||
{
|
||||
// though this didn't error before the flag, it seems as though it should error since fields of a table are invariant.
|
||||
// the user's intent would likely be that these "method" fields would be read-only, but without an annotation, accepting this should be
|
||||
// unsound.
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected =
|
||||
"Type 'Policies' from 'MainModule' could not be converted into 'Policies' from 'MainModule'"
|
||||
|
@ -1166,31 +1169,6 @@ TEST_CASE_FIXTURE(Fixture, "cli_50041_committing_txnlog_in_apollo_client_error")
|
|||
"Table type 'FieldSpecifier' not compatible with type '{| from: number? |}' because the former has extra field 'fieldName'";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauInstantiateInSubtyping)
|
||||
{
|
||||
// though this didn't error before the flag, it seems as though it should error since fields of a table are invariant.
|
||||
// the user's intent would likely be that these "method" fields would be read-only, but without an annotation, accepting this should be
|
||||
// unsound.
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
const std::string expected = R"(Type 'Policies' from 'MainModule' could not be converted into 'Policies' from 'MainModule'
|
||||
caused by:
|
||||
Property 'getStoreFieldName' is not compatible.
|
||||
Type
|
||||
'(Policies, FieldSpecifier & {| from: number? |}) -> (a, b...)'
|
||||
could not be converted into
|
||||
'(Policies, FieldSpecifier) -> string'
|
||||
caused by:
|
||||
Argument #2 type is not compatible.
|
||||
Type
|
||||
'FieldSpecifier'
|
||||
could not be converted into
|
||||
'FieldSpecifier & {| from: number? |}'
|
||||
caused by:
|
||||
Not all intersection parts are compatible.
|
||||
Table type 'FieldSpecifier' not compatible with type '{| from: number? |}' because the former has extra field 'fieldName')";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
@ -1235,19 +1213,27 @@ TEST_CASE_FIXTURE(Fixture, "type_infer_recursion_limit_normalizer")
|
|||
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(3 == result.errors.size());
|
||||
if (FFlag::LuauAstTypeGroup3)
|
||||
if (FFlag::LuauReportSubtypingErrors)
|
||||
{
|
||||
CHECK(4 == result.errors.size());
|
||||
CHECK(Location{{2, 22}, {2, 42}} == result.errors[0].location);
|
||||
CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location);
|
||||
CHECK(Location{{3, 45}, {3, 46}} == result.errors[2].location);
|
||||
CHECK(Location{{3, 22}, {3, 41}} == result.errors[3].location);
|
||||
|
||||
for (const TypeError& e : result.errors)
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(e));
|
||||
}
|
||||
else
|
||||
CHECK(Location{{2, 22}, {2, 41}} == result.errors[0].location);
|
||||
CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location);
|
||||
if (FFlag::LuauAstTypeGroup3)
|
||||
{
|
||||
CHECK(3 == result.errors.size());
|
||||
CHECK(Location{{2, 22}, {2, 42}} == result.errors[0].location);
|
||||
CHECK(Location{{3, 22}, {3, 42}} == result.errors[1].location);
|
||||
CHECK(Location{{3, 22}, {3, 41}} == result.errors[2].location);
|
||||
else
|
||||
CHECK(Location{{3, 23}, {3, 40}} == result.errors[2].location);
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[1]));
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[2]));
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[0]));
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[1]));
|
||||
CHECK_EQ("Code is too complex to typecheck! Consider simplifying the code around this area", toString(result.errors[2]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1994,7 +1980,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_table_freeze_constraint_solving")
|
|||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauMagicFreezeCheckBlocked, true}
|
||||
{FFlag::LuauMagicFreezeCheckBlocked2, true}
|
||||
};
|
||||
LUAU_REQUIRE_NO_ERRORS(check(R"(
|
||||
local f = table.freeze
|
||||
|
@ -2006,7 +1992,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_assert_table_freeze_constraint_solving"
|
|||
{
|
||||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, true},
|
||||
{FFlag::LuauMagicFreezeCheckBlocked, true}
|
||||
{FFlag::LuauMagicFreezeCheckBlocked2, true}
|
||||
};
|
||||
// This is the original fuzzer version of the above issue.
|
||||
CheckResult results = check(R"(
|
||||
|
@ -2029,7 +2015,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "cyclic_unification_aborts_eventually" * doct
|
|||
ScopedFastFlag sffs[] = {
|
||||
{FFlag::LuauSolverV2, false},
|
||||
{FFlag::LuauInstantiateInSubtyping, true},
|
||||
{FFlag::LuauLimitIterationWhenCheckingArgumentCounts, true},
|
||||
};
|
||||
|
||||
ScopedFastInt sfi{FInt::LuauTypeInferTypePackLoopLimit, 100};
|
||||
|
@ -2131,4 +2116,55 @@ local _
|
|||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzz_missing_follow_table_freeze")
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauMagicFreezeCheckBlocked2, true};
|
||||
|
||||
LUAU_REQUIRE_ERRORS(check(R"(
|
||||
if _:freeze(_)[_][_] then
|
||||
else
|
||||
do end
|
||||
end
|
||||
if _:freeze((nil))[_][_] then
|
||||
else
|
||||
do end
|
||||
end
|
||||
_ = table,true,_(lower)
|
||||
do end
|
||||
_:freeze()[_] += {} > _
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "fuzzer_avoid_double_negation" * doctest::timeout(0.5))
|
||||
{
|
||||
ScopedFastFlag _{FFlag::LuauAvoidDoubleNegation, true};
|
||||
// We don't care about errors, only that we don't OOM during typechecking.
|
||||
LUAU_REQUIRE_ERRORS(check(R"(
|
||||
local _ = _
|
||||
repeat
|
||||
do end
|
||||
while 0 do
|
||||
do
|
||||
_ = _[0]
|
||||
_._ *= _
|
||||
end
|
||||
if _ then
|
||||
elseif "" then
|
||||
end
|
||||
_ = _[0]
|
||||
_ = ""
|
||||
end
|
||||
_ = ""
|
||||
until _
|
||||
while false do
|
||||
do
|
||||
_ = _[0]
|
||||
do end
|
||||
end
|
||||
_ = ""
|
||||
return if _ then _,_
|
||||
end
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -12,8 +12,9 @@ using namespace Luau;
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
|
||||
LUAU_FASTFLAG(LuauInstantiateInSubtyping)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||
LUAU_FASTFLAG(LuauTrackInferredFunctionTypeFromCall)
|
||||
|
||||
TEST_SUITE_BEGIN("TypePackTests");
|
||||
|
||||
|
@ -957,43 +958,25 @@ a = b
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
|
||||
const std::string expected = "Type\n\t"
|
||||
"'() -> (number, ...boolean)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'() -> (number, ...string)'; \n"
|
||||
"this is because it returns a tail of the variadic `boolean` in the former type and `string` in the latter "
|
||||
"type, and `boolean` is not a subtype of `string`";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'() -> (number, ...boolean)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'() -> (number, ...string)'; \n"
|
||||
"this is because it returns a tail of the variadic `boolean` in the former type and `string` in the latter "
|
||||
"type, and `boolean` is not a subtype of `string`";
|
||||
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type\n"
|
||||
" '() -> (number, ...boolean)'\n"
|
||||
"could not be converted into\n"
|
||||
" '() -> (number, ...string)'; at returns().tail().variadic(), boolean is not a subtype of string";
|
||||
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'() -> (number, ...boolean)'
|
||||
could not be converted into
|
||||
'() -> (number, ...string)'
|
||||
caused by:
|
||||
Type 'boolean' could not be converted into 'string')";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'() -> (number, ...boolean)'
|
||||
could not be converted into
|
||||
'() -> (number, ...string)'
|
||||
caused by:
|
||||
Type 'boolean' could not be converted into 'string')";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
|
@ -1077,6 +1060,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "detect_cyclic_typepacks")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "detect_cyclic_typepacks2")
|
||||
{
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauReportSubtypingErrors, true}, {FFlag::LuauTrackInferredFunctionTypeFromCall, true}};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function _(l0:((typeof((pcall)))|((((t0)->())|(typeof(-67108864)))|(any)))|(any),...):(((typeof(0))|(any))|(any),typeof(-67108864),any)
|
||||
xpcall(_,_,_)
|
||||
|
@ -1121,16 +1106,11 @@ TEST_CASE_FIXTURE(Fixture, "unify_variadic_tails_in_arguments_free")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
CHECK(
|
||||
toString(result.errors.at(0)) == "Type pack '...number' could not be converted into 'boolean'; \nthis is because it has a tail of "
|
||||
"`...number`, which is not a subtype of `boolean`"
|
||||
);
|
||||
else if (FFlag::LuauSolverV2)
|
||||
CHECK(
|
||||
toString(result.errors.at(0)) ==
|
||||
"Type pack '...number' could not be converted into 'boolean'; type ...number.tail() (...number) is not a subtype of boolean (boolean)"
|
||||
);
|
||||
else
|
||||
CHECK_EQ(toString(result.errors[0]), "Type 'number' could not be converted into 'boolean'");
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ LUAU_FASTFLAG(LuauSolverV2)
|
|||
LUAU_FASTFLAG(LuauRefineWaitForBlockedTypesInTarget)
|
||||
LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType)
|
||||
LUAU_FASTFLAG(LuauDfgIfBlocksShouldRespectControlFlow)
|
||||
LUAU_FASTFLAG(LuauReportSubtypingErrors)
|
||||
LUAU_FASTFLAG(LuauNonReentrantGeneralization2)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -407,9 +409,7 @@ TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions")
|
|||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "prototyped_recursive_functions_but_has_future_assignments")
|
||||
{
|
||||
// early return if the flag isn't set since this is blocking gated commits
|
||||
if (!FFlag::LuauSolverV2)
|
||||
return;
|
||||
ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauReportSubtypingErrors, true}, {FFlag::LuauNonReentrantGeneralization2, true}};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local f
|
||||
|
@ -846,4 +846,4 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assign_in_an_if_branch_without_else")
|
|||
CHECK_EQ("string?", toString(requireTypeAtPosition({9, 14})));
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
TEST_SUITE_END();
|
||||
|
|
|
@ -11,7 +11,6 @@ using namespace Luau;
|
|||
LUAU_FASTFLAG(LuauSolverV2)
|
||||
|
||||
LUAU_FASTFLAG(DebugLuauGreedyGeneralization)
|
||||
LUAU_FASTFLAG(LuauImproveTypePathsInErrors)
|
||||
|
||||
TEST_SUITE_BEGIN("UnionTypes");
|
||||
|
||||
|
@ -541,7 +540,7 @@ end
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
|
||||
CHECK_EQ(
|
||||
|
@ -553,16 +552,6 @@ end
|
|||
" * the 3rd component of the union is `Z`, which is not a subtype of `{ w: number }`"
|
||||
);
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK_EQ(
|
||||
toString(result.errors[0]),
|
||||
"Type 'X | Y | Z' could not be converted into '{ w: number }'; type X | Y | Z[0] (X) is not a subtype "
|
||||
"of { w: number } ({ w: number })\n\t"
|
||||
"type X | Y | Z[1] (Y) is not a subtype of { w: number } ({ w: number })\n\t"
|
||||
"type X | Y | Z[2] (Z) is not a subtype of { w: number } ({ w: number })"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'X | Y | Z' could not be converted into '{| w: number |}'
|
||||
|
@ -683,22 +672,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_union_write_indirect")
|
|||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
// NOTE: union normalization will improve this message
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(string) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> string) | ((number) -> string)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'(string) -> number'
|
||||
could not be converted into
|
||||
'((number) -> string) | ((number) -> string)'; none of the union options are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(string) -> number'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> string) | ((number) -> string)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union_true_and_false")
|
||||
|
@ -785,22 +764,12 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generic_typepacks")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number, a...) -> (number?, a...)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> number) | ((number?, a...) -> (number?, a...))'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'(number, a...) -> (number?, a...)'
|
||||
could not be converted into
|
||||
'((number) -> number) | ((number?, a...) -> (number?, a...))'; none of the union options are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number, a...) -> (number?, a...)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> number) | ((number?, a...) -> (number?, a...))'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_arities")
|
||||
|
@ -816,22 +785,12 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_arities")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number) -> number?'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> nil) | ((number, string?) -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'(number) -> number?'
|
||||
could not be converted into
|
||||
'((number) -> nil) | ((number, string?) -> number)'; none of the union options are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number) -> number?'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((number) -> nil) | ((number, string?) -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_arities")
|
||||
|
@ -847,22 +806,12 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_arities")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'() -> number | string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(() -> (string, string)) | (() -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'() -> number | string'
|
||||
could not be converted into
|
||||
'(() -> (string, string)) | (() -> number)'; none of the union options are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'() -> number | string'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(() -> (string, string)) | (() -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_variadics")
|
||||
|
@ -878,22 +827,12 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_variadics")
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(...nil) -> (...number?)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((...string?) -> (...number)) | ((...string?) -> nil)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'(...nil) -> (...number?)'
|
||||
could not be converted into
|
||||
'((...string?) -> (...number)) | ((...string?) -> nil)'; none of the union options are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(...nil) -> (...number?)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((...string?) -> (...number)) | ((...string?) -> nil)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_variadics")
|
||||
|
@ -906,22 +845,16 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_variadics")
|
|||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
if (FFlag::LuauSolverV2 && FFlag::LuauImproveTypePathsInErrors)
|
||||
if (FFlag::LuauSolverV2)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'(number) -> ()'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((...number?) -> ()) | ((number?) -> ())'";
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'(number) -> ()'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'((...number?) -> ()) | ((number?) -> ())'";
|
||||
CHECK(expected == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauSolverV2)
|
||||
{
|
||||
CHECK(R"(Type
|
||||
'(number) -> ()'
|
||||
could not be converted into
|
||||
'((...number?) -> ()) | ((number?) -> ())')" == toString(result.errors[0]));
|
||||
}
|
||||
else if (FFlag::LuauImproveTypePathsInErrors)
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'(number) -> ()'
|
||||
|
@ -929,14 +862,6 @@ could not be converted into
|
|||
'((...number?) -> ()) | ((number?) -> ())'; none of the union options are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'(number) -> ()'
|
||||
could not be converted into
|
||||
'((...number?) -> ()) | ((number?) -> ())'; none of the union options are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_variadics")
|
||||
|
@ -952,22 +877,12 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_variadics
|
|||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
if (FFlag::LuauImproveTypePathsInErrors)
|
||||
{
|
||||
const std::string expected = "Type\n\t"
|
||||
"'() -> (number?, ...number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(() -> (...number)) | (() -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string expected = R"(Type
|
||||
'() -> (number?, ...number)'
|
||||
could not be converted into
|
||||
'(() -> (...number)) | (() -> number)'; none of the union options are compatible)";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
const std::string expected =
|
||||
"Type\n\t"
|
||||
"'() -> (number?, ...number)'"
|
||||
"\ncould not be converted into\n\t"
|
||||
"'(() -> (...number)) | (() -> number)'; none of the union options are compatible";
|
||||
CHECK_EQ(expected, toString(result.errors[0]));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "less_greedy_unification_with_union_types")
|
||||
|
|
3
tests/require/without_config/proxy_requirer.luau
Normal file
3
tests/require/without_config/proxy_requirer.luau
Normal file
|
@ -0,0 +1,3 @@
|
|||
local result = proxyrequire("./dependency", "@"..debug.info(1, "s"))
|
||||
result[#result+1] = "required into proxy_requirer"
|
||||
return result
|
Loading…
Add table
Reference in a new issue