mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Sync to upstream/release/607 (#1131)
# What's changed? * Fix up the `std::iterator_traits` definitions for some Luau data structures. * Replace some of the usages of `std::unordered_set` and `std::unordered_map` with Luau-provided data structures to increase performance and reduce overall number of heap allocations. * Update some of the documentation links in comments throughout the codebase to correctly point to the moved repository. * Expanded JSON encoder for AST to support singleton types. * Fixed a bug in `luau-analyze` where exceptions in the last module being checked during multithreaded analysis would not be rethrown. ### New type solver * Introduce a `refine` type family to handle deferred refinements during type inference, replacing the old `RefineConstraint`. * Continued work on the implementation of type states, fixing some known bugs/blockers. * Added support for variadic functions in new non-strict mode, enabling broader support for builtins and the Roblox API. ### Internal Contributors Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Vighnesh <vvijay@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
parent
2173938eb0
commit
ff502f0943
@ -197,24 +197,6 @@ struct UnpackConstraint
|
|||||||
bool resultIsLValue = false;
|
bool resultIsLValue = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// resultType ~ refine type mode discriminant
|
|
||||||
//
|
|
||||||
// Compute type & discriminant (or type | discriminant) as soon as possible (but
|
|
||||||
// no sooner), simplify, and bind resultType to that type.
|
|
||||||
struct RefineConstraint
|
|
||||||
{
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
Intersection,
|
|
||||||
Union
|
|
||||||
} mode;
|
|
||||||
|
|
||||||
TypeId resultType;
|
|
||||||
|
|
||||||
TypeId type;
|
|
||||||
TypeId discriminant;
|
|
||||||
};
|
|
||||||
|
|
||||||
// resultType ~ T0 op T1 op ... op TN
|
// resultType ~ T0 op T1 op ... op TN
|
||||||
//
|
//
|
||||||
// op is either union or intersection. If any of the input types are blocked,
|
// op is either union or intersection. If any of the input types are blocked,
|
||||||
@ -249,7 +231,7 @@ struct ReducePackConstraint
|
|||||||
|
|
||||||
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, IterableConstraint,
|
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, IterableConstraint,
|
||||||
NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, PrimitiveTypeConstraint, HasPropConstraint, SetPropConstraint,
|
NameConstraint, TypeAliasExpansionConstraint, FunctionCallConstraint, PrimitiveTypeConstraint, HasPropConstraint, SetPropConstraint,
|
||||||
SetIndexerConstraint, SingletonOrTopTypeConstraint, UnpackConstraint, RefineConstraint, SetOpConstraint, ReduceConstraint, ReducePackConstraint>;
|
SetIndexerConstraint, SingletonOrTopTypeConstraint, UnpackConstraint, SetOpConstraint, ReduceConstraint, ReducePackConstraint>;
|
||||||
|
|
||||||
struct Constraint
|
struct Constraint
|
||||||
{
|
{
|
||||||
|
@ -132,7 +132,6 @@ struct ConstraintSolver
|
|||||||
bool tryDispatch(const SetIndexerConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatch(const SetIndexerConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
bool tryDispatch(const SingletonOrTopTypeConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const SingletonOrTopTypeConstraint& c, NotNull<const Constraint> constraint);
|
||||||
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
|
bool tryDispatch(const UnpackConstraint& c, NotNull<const Constraint> constraint);
|
||||||
bool tryDispatch(const RefineConstraint& c, NotNull<const Constraint> constraint, bool force);
|
|
||||||
bool tryDispatch(const SetOpConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatch(const SetOpConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatch(const ReduceConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
|
bool tryDispatch(const ReducePackConstraint& c, NotNull<const Constraint> constraint, bool force);
|
||||||
|
@ -137,8 +137,8 @@ private:
|
|||||||
DfgScope* childScope(DfgScope* scope, DfgScope::ScopeType scopeType = DfgScope::Linear);
|
DfgScope* childScope(DfgScope* scope, DfgScope::ScopeType scopeType = DfgScope::Linear);
|
||||||
|
|
||||||
void join(DfgScope* p, DfgScope* a, DfgScope* b);
|
void join(DfgScope* p, DfgScope* a, DfgScope* b);
|
||||||
void joinBindings(DfgScope::Bindings& p, const DfgScope::Bindings& a, const DfgScope::Bindings& b);
|
void joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b);
|
||||||
void joinProps(DfgScope::Props& p, const DfgScope::Props& a, const DfgScope::Props& b);
|
void joinProps(DfgScope* p, const DfgScope& a, const DfgScope& b);
|
||||||
|
|
||||||
DefId lookup(DfgScope* scope, Symbol symbol);
|
DefId lookup(DfgScope* scope, Symbol symbol);
|
||||||
DefId lookup(DfgScope* scope, DefId def, const std::string& key);
|
DefId lookup(DfgScope* scope, DefId def, const std::string& key);
|
||||||
|
@ -118,6 +118,12 @@ public:
|
|||||||
class const_iterator
|
class const_iterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using reference = T&;
|
||||||
|
using pointer = T*;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
const_iterator(typename Impl::const_iterator impl, typename Impl::const_iterator end)
|
const_iterator(typename Impl::const_iterator impl, typename Impl::const_iterator end)
|
||||||
: impl(impl)
|
: impl(impl)
|
||||||
, end(end)
|
, end(end)
|
||||||
|
@ -176,7 +176,7 @@ struct PrimitiveType
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Singleton types https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md
|
// Singleton types https://github.com/luau-lang/rfcs/blob/master/docs/syntax-singleton-types.md
|
||||||
// Types for true and false
|
// Types for true and false
|
||||||
struct BooleanSingleton
|
struct BooleanSingleton
|
||||||
{
|
{
|
||||||
|
@ -162,6 +162,8 @@ struct BuiltinTypeFamilies
|
|||||||
TypeFamily leFamily;
|
TypeFamily leFamily;
|
||||||
TypeFamily eqFamily;
|
TypeFamily eqFamily;
|
||||||
|
|
||||||
|
TypeFamily refineFamily;
|
||||||
|
|
||||||
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
|
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -200,6 +200,23 @@ struct AstJsonEncoder : public AstVisitor
|
|||||||
{
|
{
|
||||||
writeString(name.value ? name.value : "");
|
writeString(name.value ? name.value : "");
|
||||||
}
|
}
|
||||||
|
void write(std::optional<AstArgumentName> name)
|
||||||
|
{
|
||||||
|
if (name)
|
||||||
|
write(*name);
|
||||||
|
else
|
||||||
|
writeRaw("null");
|
||||||
|
}
|
||||||
|
void write(AstArgumentName name)
|
||||||
|
{
|
||||||
|
writeRaw("{");
|
||||||
|
bool c = pushComma();
|
||||||
|
writeType("AstArgumentName");
|
||||||
|
write("name", name.first);
|
||||||
|
write("location", name.second);
|
||||||
|
popComma(c);
|
||||||
|
writeRaw("}");
|
||||||
|
}
|
||||||
|
|
||||||
void write(const Position& position)
|
void write(const Position& position)
|
||||||
{
|
{
|
||||||
@ -848,6 +865,7 @@ struct AstJsonEncoder : public AstVisitor
|
|||||||
PROP(generics);
|
PROP(generics);
|
||||||
PROP(genericPacks);
|
PROP(genericPacks);
|
||||||
PROP(argTypes);
|
PROP(argTypes);
|
||||||
|
PROP(argNames);
|
||||||
PROP(returnTypes);
|
PROP(returnTypes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -902,6 +920,22 @@ struct AstJsonEncoder : public AstVisitor
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(class AstTypeSingletonBool* node) override
|
||||||
|
{
|
||||||
|
writeNode(node, "AstTypeSingletonBool", [&]() {
|
||||||
|
write("value", node->value);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(class AstTypeSingletonString* node) override
|
||||||
|
{
|
||||||
|
writeNode(node, "AstTypeSingletonString", [&]() {
|
||||||
|
write("value", node->value);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit(class AstExprGroup* node) override
|
bool visit(class AstExprGroup* node) override
|
||||||
{
|
{
|
||||||
write(node);
|
write(node);
|
||||||
|
@ -257,7 +257,7 @@ void ConstraintGenerator::unionRefinements(const RefinementContext& lhs, const R
|
|||||||
return types[0];
|
return types[0];
|
||||||
else if (2 == types.size())
|
else if (2 == types.size())
|
||||||
{
|
{
|
||||||
// TODO: It may be advantageous to create a RefineConstraint here when there are blockedTypes.
|
// TODO: It may be advantageous to introduce a refine type family here when there are blockedTypes.
|
||||||
SimplifyResult sr = simplifyIntersection(builtinTypes, arena, types[0], types[1]);
|
SimplifyResult sr = simplifyIntersection(builtinTypes, arena, types[0], types[1]);
|
||||||
if (sr.blockedTypes.empty())
|
if (sr.blockedTypes.empty())
|
||||||
return sr.result;
|
return sr.result;
|
||||||
@ -441,10 +441,14 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat
|
|||||||
{
|
{
|
||||||
if (mustDeferIntersection(ty) || mustDeferIntersection(dt))
|
if (mustDeferIntersection(ty) || mustDeferIntersection(dt))
|
||||||
{
|
{
|
||||||
TypeId r = arena->addType(BlockedType{});
|
TypeId resultType = arena->addType(TypeFamilyInstanceType{
|
||||||
addConstraint(scope, location, RefineConstraint{RefineConstraint::Intersection, r, ty, dt});
|
NotNull{&kBuiltinTypeFamilies.refineFamily},
|
||||||
|
{ty, dt},
|
||||||
|
{},
|
||||||
|
});
|
||||||
|
addConstraint(scope, location, ReduceConstraint{resultType});
|
||||||
|
|
||||||
ty = r;
|
ty = resultType;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1005,9 +1009,6 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatAssign* ass
|
|||||||
|
|
||||||
checkLValue(scope, lvalue, assignee);
|
checkLValue(scope, lvalue, assignee);
|
||||||
assignees.push_back(assignee);
|
assignees.push_back(assignee);
|
||||||
|
|
||||||
DefId def = dfg->getDef(lvalue);
|
|
||||||
scope->lvalueTypes[def] = assignee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePackId resultPack = checkPack(scope, assign->values).tp;
|
TypePackId resultPack = checkPack(scope, assign->values).tp;
|
||||||
|
@ -545,8 +545,6 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
|
|||||||
success = tryDispatch(*sottc, constraint);
|
success = tryDispatch(*sottc, constraint);
|
||||||
else if (auto uc = get<UnpackConstraint>(*constraint))
|
else if (auto uc = get<UnpackConstraint>(*constraint))
|
||||||
success = tryDispatch(*uc, constraint);
|
success = tryDispatch(*uc, constraint);
|
||||||
else if (auto rc = get<RefineConstraint>(*constraint))
|
|
||||||
success = tryDispatch(*rc, constraint, force);
|
|
||||||
else if (auto soc = get<SetOpConstraint>(*constraint))
|
else if (auto soc = get<SetOpConstraint>(*constraint))
|
||||||
success = tryDispatch(*soc, constraint, force);
|
success = tryDispatch(*soc, constraint, force);
|
||||||
else if (auto rc = get<ReduceConstraint>(*constraint))
|
else if (auto rc = get<ReduceConstraint>(*constraint))
|
||||||
@ -887,9 +885,9 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
|||||||
// In order to prevent infinite types from being expanded and causing us to
|
// In order to prevent infinite types from being expanded and causing us to
|
||||||
// cycle infinitely, we need to scan the type function for cases where we
|
// cycle infinitely, we need to scan the type function for cases where we
|
||||||
// expand the same alias with different type saturatedTypeArguments. See
|
// expand the same alias with different type saturatedTypeArguments. See
|
||||||
// https://github.com/Roblox/luau/pull/68 for the RFC responsible for this.
|
// https://github.com/luau-lang/luau/pull/68 for the RFC responsible for
|
||||||
// This is a little nicer than using a recursion limit because we can catch
|
// this. This is a little nicer than using a recursion limit because we can
|
||||||
// the infinite expansion before actually trying to expand it.
|
// catch the infinite expansion before actually trying to expand it.
|
||||||
InfiniteTypeFinder itf{this, signature, constraint->scope};
|
InfiniteTypeFinder itf{this, signature, constraint->scope};
|
||||||
itf.traverse(tf->type);
|
itf.traverse(tf->type);
|
||||||
|
|
||||||
@ -1505,151 +1503,6 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Search for types that prevent us from being ready to dispatch a particular
|
|
||||||
* RefineConstraint.
|
|
||||||
*/
|
|
||||||
struct FindRefineConstraintBlockers : TypeOnceVisitor
|
|
||||||
{
|
|
||||||
DenseHashSet<TypeId> found{nullptr};
|
|
||||||
bool visit(TypeId ty, const BlockedType&) override
|
|
||||||
{
|
|
||||||
found.insert(ty);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const PendingExpansionType&) override
|
|
||||||
{
|
|
||||||
found.insert(ty);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool visit(TypeId ty, const ClassType&) override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
static bool isNegatedAny(TypeId ty)
|
|
||||||
{
|
|
||||||
ty = follow(ty);
|
|
||||||
const NegationType* nt = get<NegationType>(ty);
|
|
||||||
if (!nt)
|
|
||||||
return false;
|
|
||||||
TypeId negatedTy = follow(nt->ty);
|
|
||||||
return bool(get<AnyType>(negatedTy));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConstraintSolver::tryDispatch(const RefineConstraint& c, NotNull<const Constraint> constraint, bool force)
|
|
||||||
{
|
|
||||||
if (isBlocked(c.discriminant))
|
|
||||||
return block(c.discriminant, constraint);
|
|
||||||
|
|
||||||
FindRefineConstraintBlockers fbt;
|
|
||||||
fbt.traverse(c.discriminant);
|
|
||||||
|
|
||||||
if (!fbt.found.empty())
|
|
||||||
{
|
|
||||||
bool foundOne = false;
|
|
||||||
|
|
||||||
for (TypeId blocked : fbt.found)
|
|
||||||
{
|
|
||||||
if (blocked == c.type)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
block(blocked, constraint);
|
|
||||||
foundOne = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundOne)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HACK: Refinements sometimes produce a type T & ~any under the assumption
|
|
||||||
* that ~any is the same as any. This is so so weird, but refinements needs
|
|
||||||
* some way to say "I may refine this, but I'm not sure."
|
|
||||||
*
|
|
||||||
* It does this by refining on a blocked type and deferring the decision
|
|
||||||
* until it is unblocked.
|
|
||||||
*
|
|
||||||
* Refinements also get negated, so we wind up with types like T & ~*blocked*
|
|
||||||
*
|
|
||||||
* We need to treat T & ~any as T in this case.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (c.mode == RefineConstraint::Intersection && isNegatedAny(c.discriminant))
|
|
||||||
{
|
|
||||||
asMutable(c.resultType)->ty.emplace<BoundType>(c.type);
|
|
||||||
unblock(c.resultType, constraint->location);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TypeId type = follow(c.type);
|
|
||||||
|
|
||||||
if (hasUnresolvedConstraints(type))
|
|
||||||
return block(type, constraint);
|
|
||||||
|
|
||||||
LUAU_ASSERT(get<BlockedType>(c.resultType));
|
|
||||||
|
|
||||||
if (type == c.resultType)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Sometimes, we get a constraint of the form
|
|
||||||
*
|
|
||||||
* *blocked-N* ~ refine *blocked-N* & U
|
|
||||||
*
|
|
||||||
* The constraint essentially states that a particular type is a
|
|
||||||
* refinement of itself. This is weird and I think vacuous.
|
|
||||||
*
|
|
||||||
* I *believe* it is safe to replace the result with a fresh type that
|
|
||||||
* is constrained by U. We effect this by minting a fresh type for the
|
|
||||||
* result when U = any, else we bind the result to whatever discriminant
|
|
||||||
* was offered.
|
|
||||||
*/
|
|
||||||
if (get<AnyType>(follow(c.discriminant)))
|
|
||||||
{
|
|
||||||
TypeId f = freshType(arena, builtinTypes, constraint->scope);
|
|
||||||
asMutable(c.resultType)->ty.emplace<BoundType>(f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
asMutable(c.resultType)->ty.emplace<BoundType>(c.discriminant);
|
|
||||||
|
|
||||||
unblock(c.resultType, constraint->location);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto [result, blockedTypes] = c.mode == RefineConstraint::Intersection ? simplifyIntersection(builtinTypes, NotNull{arena}, type, c.discriminant)
|
|
||||||
: simplifyUnion(builtinTypes, NotNull{arena}, type, c.discriminant);
|
|
||||||
|
|
||||||
if (!force && !blockedTypes.empty())
|
|
||||||
return block(blockedTypes, constraint);
|
|
||||||
|
|
||||||
switch (shouldSuppressErrors(normalizer, c.type))
|
|
||||||
{
|
|
||||||
case ErrorSuppression::Suppress:
|
|
||||||
{
|
|
||||||
auto resultOrError = simplifyUnion(builtinTypes, arena, result, builtinTypes->errorType).result;
|
|
||||||
asMutable(c.resultType)->ty.emplace<BoundType>(resultOrError);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ErrorSuppression::DoNotSuppress:
|
|
||||||
asMutable(c.resultType)->ty.emplace<BoundType>(result);
|
|
||||||
break;
|
|
||||||
case ErrorSuppression::NormalizationFailed:
|
|
||||||
reportError(NormalizationTooComplex{}, constraint->location);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
unblock(c.resultType, constraint->location);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConstraintSolver::tryDispatch(const SetOpConstraint& c, NotNull<const Constraint> constraint, bool force)
|
bool ConstraintSolver::tryDispatch(const SetOpConstraint& c, NotNull<const Constraint> constraint, bool force)
|
||||||
{
|
{
|
||||||
bool blocked = false;
|
bool blocked = false;
|
||||||
|
@ -180,36 +180,39 @@ DfgScope* DataFlowGraphBuilder::childScope(DfgScope* scope, DfgScope::ScopeType
|
|||||||
|
|
||||||
void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b)
|
void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b)
|
||||||
{
|
{
|
||||||
joinBindings(p->bindings, a->bindings, b->bindings);
|
joinBindings(p, *a, *b);
|
||||||
joinProps(p->props, a->props, b->props);
|
joinProps(p, *a, *b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowGraphBuilder::joinBindings(DfgScope::Bindings& p, const DfgScope::Bindings& a, const DfgScope::Bindings& b)
|
void DataFlowGraphBuilder::joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b)
|
||||||
{
|
{
|
||||||
for (const auto& [sym, def1] : a)
|
for (const auto& [sym, def1] : a.bindings)
|
||||||
{
|
{
|
||||||
if (auto def2 = b.find(sym))
|
if (auto def2 = b.bindings.find(sym))
|
||||||
p[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
|
p->bindings[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
|
||||||
else if (auto def2 = p.find(sym))
|
else if (auto def2 = p->lookup(sym))
|
||||||
p[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
|
p->bindings[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [sym, def1] : b)
|
for (const auto& [sym, def1] : b.bindings)
|
||||||
{
|
{
|
||||||
if (auto def2 = p.find(sym))
|
if (auto def2 = p->lookup(sym))
|
||||||
p[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
|
p->bindings[sym] = defArena->phi(NotNull{def1}, NotNull{*def2});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFlowGraphBuilder::joinProps(DfgScope::Props& p, const DfgScope::Props& a, const DfgScope::Props& b)
|
void DataFlowGraphBuilder::joinProps(DfgScope* result, const DfgScope& a, const DfgScope& b)
|
||||||
{
|
{
|
||||||
auto phinodify = [this](auto& p, const auto& a, const auto& b) mutable {
|
auto phinodify = [this](DfgScope* scope, const auto& a, const auto& b, DefId parent) mutable {
|
||||||
|
auto& p = scope->props[parent];
|
||||||
for (const auto& [k, defA] : a)
|
for (const auto& [k, defA] : a)
|
||||||
{
|
{
|
||||||
if (auto it = b.find(k); it != b.end())
|
if (auto it = b.find(k); it != b.end())
|
||||||
p[k] = defArena->phi(NotNull{it->second}, NotNull{defA});
|
p[k] = defArena->phi(NotNull{it->second}, NotNull{defA});
|
||||||
else if (auto it = p.find(k); it != p.end())
|
else if (auto it = p.find(k); it != p.end())
|
||||||
p[k] = defArena->phi(NotNull{it->second}, NotNull{defA});
|
p[k] = defArena->phi(NotNull{it->second}, NotNull{defA});
|
||||||
|
else if (auto def2 = scope->lookup(parent, k))
|
||||||
|
p[k] = defArena->phi(*def2, NotNull{defA});
|
||||||
else
|
else
|
||||||
p[k] = defA;
|
p[k] = defA;
|
||||||
}
|
}
|
||||||
@ -220,27 +223,29 @@ void DataFlowGraphBuilder::joinProps(DfgScope::Props& p, const DfgScope::Props&
|
|||||||
continue;
|
continue;
|
||||||
else if (auto it = p.find(k); it != p.end())
|
else if (auto it = p.find(k); it != p.end())
|
||||||
p[k] = defArena->phi(NotNull{it->second}, NotNull{defB});
|
p[k] = defArena->phi(NotNull{it->second}, NotNull{defB});
|
||||||
|
else if (auto def2 = scope->lookup(parent, k))
|
||||||
|
p[k] = defArena->phi(*def2, NotNull{defB});
|
||||||
else
|
else
|
||||||
p[k] = defB;
|
p[k] = defB;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& [def, a1] : a)
|
for (const auto& [def, a1] : a.props)
|
||||||
{
|
{
|
||||||
p.try_insert(def, {});
|
result->props.try_insert(def, {});
|
||||||
if (auto a2 = b.find(def))
|
if (auto a2 = b.props.find(def))
|
||||||
phinodify(p[def], a1, *a2);
|
phinodify(result, a1, *a2, NotNull{def});
|
||||||
else if (auto a2 = p.find(def))
|
else if (auto a2 = result->props.find(def))
|
||||||
phinodify(p[def], a1, *a2);
|
phinodify(result, a1, *a2, NotNull{def});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [def, a1] : b)
|
for (const auto& [def, a1] : b.props)
|
||||||
{
|
{
|
||||||
p.try_insert(def, {});
|
result->props.try_insert(def, {});
|
||||||
if (a.find(def))
|
if (a.props.find(def))
|
||||||
continue;
|
continue;
|
||||||
else if (auto a2 = p.find(def))
|
else if (auto a2 = result->props.find(def))
|
||||||
phinodify(p[def], a1, *a2);
|
phinodify(result, a1, *a2, NotNull{def});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,6 +471,14 @@ ControlFlow DataFlowGraphBuilder::visit(DfgScope* scope, AstStatLocal* l)
|
|||||||
// make sure that the non-aliased defs are also marked as a subscript for refinements.
|
// make sure that the non-aliased defs are also marked as a subscript for refinements.
|
||||||
bool subscripted = i < defs.size() && containsSubscriptedDefinition(defs[i]);
|
bool subscripted = i < defs.size() && containsSubscriptedDefinition(defs[i]);
|
||||||
DefId def = defArena->freshCell(subscripted);
|
DefId def = defArena->freshCell(subscripted);
|
||||||
|
if (i < l->values.size)
|
||||||
|
{
|
||||||
|
AstExpr* e = l->values.data[i];
|
||||||
|
if (const AstExprTable* tbl = e->as<AstExprTable>())
|
||||||
|
{
|
||||||
|
def = defs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
graph.localDefs[local] = def;
|
graph.localDefs[local] = def;
|
||||||
scope->bindings[local] = def;
|
scope->bindings[local] = def;
|
||||||
captures[local].allVersions.push_back(def);
|
captures[local].allVersions.push_back(def);
|
||||||
@ -769,7 +782,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprIndexExpr
|
|||||||
return {def, keyArena->node(parentKey, def, index)};
|
return {def, keyArena->node(parentKey, def, index)};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {defArena->freshCell(/* subscripted= */true), nullptr};
|
return {defArena->freshCell(/* subscripted= */ true), nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprFunction* f)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprFunction* f)
|
||||||
@ -819,14 +832,20 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprFunction*
|
|||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprTable* t)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprTable* t)
|
||||||
{
|
{
|
||||||
|
DefId tableCell = defArena->freshCell();
|
||||||
|
scope->props[tableCell] = {};
|
||||||
for (AstExprTable::Item item : t->items)
|
for (AstExprTable::Item item : t->items)
|
||||||
{
|
{
|
||||||
|
DataFlowResult result = visitExpr(scope, item.value);
|
||||||
if (item.key)
|
if (item.key)
|
||||||
|
{
|
||||||
visitExpr(scope, item.key);
|
visitExpr(scope, item.key);
|
||||||
visitExpr(scope, item.value);
|
if (auto string = item.key->as<AstExprConstantString>())
|
||||||
|
scope->props[tableCell][string->value.data] = result.def;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {defArena->freshCell(), nullptr};
|
return {tableCell, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprUnary* u)
|
DataFlowResult DataFlowGraphBuilder::visitExpr(DfgScope* scope, AstExprUnary* u)
|
||||||
|
@ -37,6 +37,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false)
|
|||||||
LUAU_FASTFLAGVARIABLE(DebugLuauReadWriteProperties, false)
|
LUAU_FASTFLAGVARIABLE(DebugLuauReadWriteProperties, false)
|
||||||
LUAU_FASTFLAGVARIABLE(CorrectEarlyReturnInMarkDirty, false)
|
LUAU_FASTFLAGVARIABLE(CorrectEarlyReturnInMarkDirty, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDefinitionFileSetModuleName, false)
|
LUAU_FASTFLAGVARIABLE(LuauDefinitionFileSetModuleName, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauRethrowSingleModuleIce, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
@ -679,8 +680,7 @@ std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptio
|
|||||||
sendItemTask(i);
|
sendItemTask(i);
|
||||||
nextItems.clear();
|
nextItems.clear();
|
||||||
|
|
||||||
// If we aren't done, but don't have anything processing, we hit a cycle
|
if (FFlag::LuauRethrowSingleModuleIce && processing == 0)
|
||||||
if (remaining != 0 && processing == 0)
|
|
||||||
{
|
{
|
||||||
// Typechecking might have been cancelled by user, don't return partial results
|
// Typechecking might have been cancelled by user, don't return partial results
|
||||||
if (cancelled)
|
if (cancelled)
|
||||||
@ -688,9 +688,24 @@ std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptio
|
|||||||
|
|
||||||
// We might have stopped because of a pending exception
|
// We might have stopped because of a pending exception
|
||||||
if (itemWithException)
|
if (itemWithException)
|
||||||
{
|
|
||||||
recordItemResult(buildQueueItems[*itemWithException]);
|
recordItemResult(buildQueueItems[*itemWithException]);
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
// If we aren't done, but don't have anything processing, we hit a cycle
|
||||||
|
if (remaining != 0 && processing == 0)
|
||||||
|
{
|
||||||
|
if (!FFlag::LuauRethrowSingleModuleIce)
|
||||||
|
{
|
||||||
|
// Typechecking might have been cancelled by user, don't return partial results
|
||||||
|
if (cancelled)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// We might have stopped because of a pending exception
|
||||||
|
if (itemWithException)
|
||||||
|
{
|
||||||
|
recordItemResult(buildQueueItems[*itemWithException]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendCycleItemTask();
|
sendCycleItemTask();
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "Luau/TypeArena.h"
|
#include "Luau/TypeArena.h"
|
||||||
#include "Luau/TypeFamily.h"
|
#include "Luau/TypeFamily.h"
|
||||||
#include "Luau/Def.h"
|
#include "Luau/Def.h"
|
||||||
|
#include "Luau/TypeFwd.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
@ -57,8 +58,6 @@ struct StackPusher
|
|||||||
|
|
||||||
struct NonStrictContext
|
struct NonStrictContext
|
||||||
{
|
{
|
||||||
std::unordered_map<const Def*, TypeId> context;
|
|
||||||
|
|
||||||
NonStrictContext() = default;
|
NonStrictContext() = default;
|
||||||
|
|
||||||
NonStrictContext(const NonStrictContext&) = delete;
|
NonStrictContext(const NonStrictContext&) = delete;
|
||||||
@ -109,7 +108,12 @@ struct NonStrictContext
|
|||||||
// Returns true if the removal was successful
|
// Returns true if the removal was successful
|
||||||
bool remove(const DefId& def)
|
bool remove(const DefId& def)
|
||||||
{
|
{
|
||||||
return context.erase(def.get()) == 1;
|
std::vector<DefId> defs;
|
||||||
|
collectOperands(def, &defs);
|
||||||
|
bool result = true;
|
||||||
|
for (DefId def : defs)
|
||||||
|
result = result && context.erase(def.get()) == 1;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TypeId> find(const DefId& def) const
|
std::optional<TypeId> find(const DefId& def) const
|
||||||
@ -118,6 +122,14 @@ struct NonStrictContext
|
|||||||
return find(d);
|
return find(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addContext(const DefId& def, TypeId ty)
|
||||||
|
{
|
||||||
|
std::vector<DefId> defs;
|
||||||
|
collectOperands(def, &defs);
|
||||||
|
for (DefId def : defs)
|
||||||
|
context[def.get()] = ty;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<TypeId> find(const Def* d) const
|
std::optional<TypeId> find(const Def* d) const
|
||||||
{
|
{
|
||||||
@ -126,6 +138,9 @@ private:
|
|||||||
return {it->second};
|
return {it->second};
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_map<const Def*, TypeId> context;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NonStrictTypeChecker
|
struct NonStrictTypeChecker
|
||||||
@ -508,8 +523,25 @@ struct NonStrictTypeChecker
|
|||||||
// ...
|
// ...
|
||||||
// (unknown^N-1, ~S_N) -> error
|
// (unknown^N-1, ~S_N) -> error
|
||||||
std::vector<TypeId> argTypes;
|
std::vector<TypeId> argTypes;
|
||||||
for (TypeId ty : fn->argTypes)
|
argTypes.reserve(call->args.size);
|
||||||
argTypes.push_back(ty);
|
// Pad out the arg types array with the types you would expect to see
|
||||||
|
TypePackIterator curr = begin(fn->argTypes);
|
||||||
|
TypePackIterator fin = end(fn->argTypes);
|
||||||
|
while (curr != fin)
|
||||||
|
{
|
||||||
|
argTypes.push_back(*curr);
|
||||||
|
++curr;
|
||||||
|
}
|
||||||
|
if (auto argTail = curr.tail())
|
||||||
|
{
|
||||||
|
if (const VariadicTypePack* vtp = get<VariadicTypePack>(follow(*argTail)))
|
||||||
|
{
|
||||||
|
while (argTypes.size() < call->args.size)
|
||||||
|
{
|
||||||
|
argTypes.push_back(vtp->ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// For a checked function, these gotta be the same size
|
// For a checked function, these gotta be the same size
|
||||||
LUAU_ASSERT(call->args.size == argTypes.size());
|
LUAU_ASSERT(call->args.size == argTypes.size());
|
||||||
for (size_t i = 0; i < call->args.size; i++)
|
for (size_t i = 0; i < call->args.size; i++)
|
||||||
@ -523,7 +555,7 @@ struct NonStrictTypeChecker
|
|||||||
TypeId expectedArgType = argTypes[i];
|
TypeId expectedArgType = argTypes[i];
|
||||||
DefId def = dfg->getDef(arg);
|
DefId def = dfg->getDef(arg);
|
||||||
TypeId runTimeErrorTy = getOrCreateNegation(expectedArgType);
|
TypeId runTimeErrorTy = getOrCreateNegation(expectedArgType);
|
||||||
fresh.context[def.get()] = runTimeErrorTy;
|
fresh.addContext(def, runTimeErrorTy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate the context and now iterate through each of the arguments to the call to find out if we satisfy the types
|
// Populate the context and now iterate through each of the arguments to the call to find out if we satisfy the types
|
||||||
@ -613,15 +645,20 @@ struct NonStrictTypeChecker
|
|||||||
std::optional<TypeId> willRunTimeError(AstExpr* fragment, const NonStrictContext& context)
|
std::optional<TypeId> willRunTimeError(AstExpr* fragment, const NonStrictContext& context)
|
||||||
{
|
{
|
||||||
DefId def = dfg->getDef(fragment);
|
DefId def = dfg->getDef(fragment);
|
||||||
if (std::optional<TypeId> contextTy = context.find(def))
|
std::vector<DefId> defs;
|
||||||
|
collectOperands(def, &defs);
|
||||||
|
for (DefId def : defs)
|
||||||
{
|
{
|
||||||
|
if (std::optional<TypeId> contextTy = context.find(def))
|
||||||
|
{
|
||||||
|
|
||||||
TypeId actualType = lookupType(fragment);
|
TypeId actualType = lookupType(fragment);
|
||||||
SubtypingResult r = subtyping.isSubtype(actualType, *contextTy);
|
SubtypingResult r = subtyping.isSubtype(actualType, *contextTy);
|
||||||
if (r.normalizationTooComplex)
|
if (r.normalizationTooComplex)
|
||||||
reportError(NormalizationTooComplex{}, fragment->location);
|
reportError(NormalizationTooComplex{}, fragment->location);
|
||||||
if (r.isSubtype)
|
if (r.isSubtype)
|
||||||
return {actualType};
|
return {actualType};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@ -630,15 +667,20 @@ struct NonStrictTypeChecker
|
|||||||
std::optional<TypeId> willRunTimeErrorFunctionDefinition(AstLocal* fragment, const NonStrictContext& context)
|
std::optional<TypeId> willRunTimeErrorFunctionDefinition(AstLocal* fragment, const NonStrictContext& context)
|
||||||
{
|
{
|
||||||
DefId def = dfg->getDef(fragment);
|
DefId def = dfg->getDef(fragment);
|
||||||
if (std::optional<TypeId> contextTy = context.find(def))
|
std::vector<DefId> defs;
|
||||||
|
collectOperands(def, &defs);
|
||||||
|
for (DefId def : defs)
|
||||||
{
|
{
|
||||||
SubtypingResult r1 = subtyping.isSubtype(builtinTypes->unknownType, *contextTy);
|
if (std::optional<TypeId> contextTy = context.find(def))
|
||||||
SubtypingResult r2 = subtyping.isSubtype(*contextTy, builtinTypes->unknownType);
|
{
|
||||||
if (r1.normalizationTooComplex || r2.normalizationTooComplex)
|
SubtypingResult r1 = subtyping.isSubtype(builtinTypes->unknownType, *contextTy);
|
||||||
reportError(NormalizationTooComplex{}, fragment->location);
|
SubtypingResult r2 = subtyping.isSubtype(*contextTy, builtinTypes->unknownType);
|
||||||
bool isUnknown = r1.isSubtype && r2.isSubtype;
|
if (r1.normalizationTooComplex || r2.normalizationTooComplex)
|
||||||
if (isUnknown)
|
reportError(NormalizationTooComplex{}, fragment->location);
|
||||||
return {builtinTypes->unknownType};
|
bool isUnknown = r1.isSubtype && r2.isSubtype;
|
||||||
|
if (isUnknown)
|
||||||
|
return {builtinTypes->unknownType};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
#include "Luau/Constraint.h"
|
#include "Luau/Constraint.h"
|
||||||
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/Location.h"
|
#include "Luau/Location.h"
|
||||||
#include "Luau/Scope.h"
|
#include "Luau/Scope.h"
|
||||||
|
#include "Luau/Set.h"
|
||||||
#include "Luau/TxnLog.h"
|
#include "Luau/TxnLog.h"
|
||||||
#include "Luau/TypeInfer.h"
|
#include "Luau/TypeInfer.h"
|
||||||
#include "Luau/TypePack.h"
|
#include "Luau/TypePack.h"
|
||||||
@ -53,8 +55,8 @@ struct FindCyclicTypes final : TypeVisitor
|
|||||||
FindCyclicTypes& operator=(const FindCyclicTypes&) = delete;
|
FindCyclicTypes& operator=(const FindCyclicTypes&) = delete;
|
||||||
|
|
||||||
bool exhaustive = false;
|
bool exhaustive = false;
|
||||||
std::unordered_set<TypeId> visited;
|
Luau::Set<TypeId> visited{{}};
|
||||||
std::unordered_set<TypePackId> visitedPacks;
|
Luau::Set<TypePackId> visitedPacks{{}};
|
||||||
std::set<TypeId> cycles;
|
std::set<TypeId> cycles;
|
||||||
std::set<TypePackId> cycleTPs;
|
std::set<TypePackId> cycleTPs;
|
||||||
|
|
||||||
@ -70,17 +72,17 @@ struct FindCyclicTypes final : TypeVisitor
|
|||||||
|
|
||||||
bool visit(TypeId ty) override
|
bool visit(TypeId ty) override
|
||||||
{
|
{
|
||||||
return visited.insert(ty).second;
|
return visited.insert(ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypePackId tp) override
|
bool visit(TypePackId tp) override
|
||||||
{
|
{
|
||||||
return visitedPacks.insert(tp).second;
|
return visitedPacks.insert(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(TypeId ty, const FreeType& ft) override
|
bool visit(TypeId ty, const FreeType& ft) override
|
||||||
{
|
{
|
||||||
if (!visited.insert(ty).second)
|
if (!visited.insert(ty))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (FFlag::DebugLuauDeferredConstraintResolution)
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
@ -102,7 +104,7 @@ struct FindCyclicTypes final : TypeVisitor
|
|||||||
|
|
||||||
bool visit(TypeId ty, const LocalType& lt) override
|
bool visit(TypeId ty, const LocalType& lt) override
|
||||||
{
|
{
|
||||||
if (!visited.insert(ty).second)
|
if (!visited.insert(ty))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
traverse(lt.domain);
|
traverse(lt.domain);
|
||||||
@ -112,7 +114,7 @@ struct FindCyclicTypes final : TypeVisitor
|
|||||||
|
|
||||||
bool visit(TypeId ty, const TableType& ttv) override
|
bool visit(TypeId ty, const TableType& ttv) override
|
||||||
{
|
{
|
||||||
if (!visited.insert(ty).second)
|
if (!visited.insert(ty))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (ttv.name || ttv.syntheticName)
|
if (ttv.name || ttv.syntheticName)
|
||||||
@ -175,10 +177,11 @@ struct StringifierState
|
|||||||
ToStringOptions& opts;
|
ToStringOptions& opts;
|
||||||
ToStringResult& result;
|
ToStringResult& result;
|
||||||
|
|
||||||
std::unordered_map<TypeId, std::string> cycleNames;
|
DenseHashMap<TypeId, std::string> cycleNames{{}};
|
||||||
std::unordered_map<TypePackId, std::string> cycleTpNames;
|
DenseHashMap<TypePackId, std::string> cycleTpNames{{}};
|
||||||
std::unordered_set<void*> seen;
|
Set<void*> seen{{}};
|
||||||
std::unordered_set<std::string> usedNames;
|
// `$$$` was chosen as the tombstone for `usedNames` since it is not a valid name syntactically and is relatively short for string comparison reasons.
|
||||||
|
DenseHashSet<std::string> usedNames{"$$$"};
|
||||||
size_t indentation = 0;
|
size_t indentation = 0;
|
||||||
|
|
||||||
bool exhaustive;
|
bool exhaustive;
|
||||||
@ -197,7 +200,7 @@ struct StringifierState
|
|||||||
bool hasSeen(const void* tv)
|
bool hasSeen(const void* tv)
|
||||||
{
|
{
|
||||||
void* ttv = const_cast<void*>(tv);
|
void* ttv = const_cast<void*>(tv);
|
||||||
if (seen.find(ttv) != seen.end())
|
if (seen.contains(ttv))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
seen.insert(ttv);
|
seen.insert(ttv);
|
||||||
@ -207,9 +210,9 @@ struct StringifierState
|
|||||||
void unsee(const void* tv)
|
void unsee(const void* tv)
|
||||||
{
|
{
|
||||||
void* ttv = const_cast<void*>(tv);
|
void* ttv = const_cast<void*>(tv);
|
||||||
auto iter = seen.find(ttv);
|
|
||||||
if (iter != seen.end())
|
if (seen.contains(ttv))
|
||||||
seen.erase(iter);
|
seen.erase(ttv);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getName(TypeId ty)
|
std::string getName(TypeId ty)
|
||||||
@ -222,7 +225,7 @@ struct StringifierState
|
|||||||
for (int count = 0; count < 256; ++count)
|
for (int count = 0; count < 256; ++count)
|
||||||
{
|
{
|
||||||
std::string candidate = generateName(usedNames.size() + count);
|
std::string candidate = generateName(usedNames.size() + count);
|
||||||
if (!usedNames.count(candidate))
|
if (!usedNames.contains(candidate))
|
||||||
{
|
{
|
||||||
usedNames.insert(candidate);
|
usedNames.insert(candidate);
|
||||||
n = candidate;
|
n = candidate;
|
||||||
@ -245,7 +248,7 @@ struct StringifierState
|
|||||||
for (int count = 0; count < 256; ++count)
|
for (int count = 0; count < 256; ++count)
|
||||||
{
|
{
|
||||||
std::string candidate = generateName(previousNameIndex + count);
|
std::string candidate = generateName(previousNameIndex + count);
|
||||||
if (!usedNames.count(candidate))
|
if (!usedNames.contains(candidate))
|
||||||
{
|
{
|
||||||
previousNameIndex += count;
|
previousNameIndex += count;
|
||||||
usedNames.insert(candidate);
|
usedNames.insert(candidate);
|
||||||
@ -358,10 +361,9 @@ struct TypeStringifier
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = state.cycleNames.find(tv);
|
if (auto p = state.cycleNames.find(tv))
|
||||||
if (it != state.cycleNames.end())
|
|
||||||
{
|
{
|
||||||
state.emit(it->second);
|
state.emit(*p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -886,7 +888,7 @@ struct TypeStringifier
|
|||||||
|
|
||||||
std::string saved = std::move(state.result.name);
|
std::string saved = std::move(state.result.name);
|
||||||
|
|
||||||
bool needParens = !state.cycleNames.count(el) && (get<IntersectionType>(el) || get<FunctionType>(el));
|
bool needParens = !state.cycleNames.contains(el) && (get<IntersectionType>(el) || get<FunctionType>(el));
|
||||||
|
|
||||||
if (needParens)
|
if (needParens)
|
||||||
state.emit("(");
|
state.emit("(");
|
||||||
@ -953,7 +955,7 @@ struct TypeStringifier
|
|||||||
|
|
||||||
std::string saved = std::move(state.result.name);
|
std::string saved = std::move(state.result.name);
|
||||||
|
|
||||||
bool needParens = !state.cycleNames.count(el) && (get<UnionType>(el) || get<FunctionType>(el));
|
bool needParens = !state.cycleNames.contains(el) && (get<UnionType>(el) || get<FunctionType>(el));
|
||||||
|
|
||||||
if (needParens)
|
if (needParens)
|
||||||
state.emit("(");
|
state.emit("(");
|
||||||
@ -1101,10 +1103,9 @@ struct TypePackStringifier
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = state.cycleTpNames.find(tp);
|
if (auto p = state.cycleTpNames.find(tp))
|
||||||
if (it != state.cycleTpNames.end())
|
|
||||||
{
|
{
|
||||||
state.emit(it->second);
|
state.emit(*p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1278,7 +1279,7 @@ void TypeStringifier::stringify(TypePackId tpid, const std::vector<std::optional
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void assignCycleNames(const std::set<TypeId>& cycles, const std::set<TypePackId>& cycleTPs,
|
static void assignCycleNames(const std::set<TypeId>& cycles, const std::set<TypePackId>& cycleTPs,
|
||||||
std::unordered_map<TypeId, std::string>& cycleNames, std::unordered_map<TypePackId, std::string>& cycleTpNames, bool exhaustive)
|
DenseHashMap<TypeId, std::string>& cycleNames, DenseHashMap<TypePackId, std::string>& cycleTpNames, bool exhaustive)
|
||||||
{
|
{
|
||||||
int nextIndex = 1;
|
int nextIndex = 1;
|
||||||
|
|
||||||
@ -1372,9 +1373,8 @@ ToStringResult toStringDetailed(TypeId ty, ToStringOptions& opts)
|
|||||||
*
|
*
|
||||||
* t1 where t1 = the_whole_root_type
|
* t1 where t1 = the_whole_root_type
|
||||||
*/
|
*/
|
||||||
auto it = state.cycleNames.find(ty);
|
if (auto p = state.cycleNames.find(ty))
|
||||||
if (it != state.cycleNames.end())
|
state.emit(*p);
|
||||||
state.emit(it->second);
|
|
||||||
else
|
else
|
||||||
tvs.stringify(ty);
|
tvs.stringify(ty);
|
||||||
|
|
||||||
@ -1466,9 +1466,8 @@ ToStringResult toStringDetailed(TypePackId tp, ToStringOptions& opts)
|
|||||||
*
|
*
|
||||||
* t1 where t1 = the_whole_root_type
|
* t1 where t1 = the_whole_root_type
|
||||||
*/
|
*/
|
||||||
auto it = state.cycleTpNames.find(tp);
|
if (auto p = state.cycleTpNames.find(tp))
|
||||||
if (it != state.cycleTpNames.end())
|
state.emit(*p);
|
||||||
state.emit(it->second);
|
|
||||||
else
|
else
|
||||||
tvs.stringify(tp);
|
tvs.stringify(tp);
|
||||||
|
|
||||||
@ -1766,11 +1765,6 @@ std::string toString(const Constraint& constraint, ToStringOptions& opts)
|
|||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<T, UnpackConstraint>)
|
else if constexpr (std::is_same_v<T, UnpackConstraint>)
|
||||||
return tos(c.resultPack) + " ~ unpack " + tos(c.sourcePack);
|
return tos(c.resultPack) + " ~ unpack " + tos(c.sourcePack);
|
||||||
else if constexpr (std::is_same_v<T, RefineConstraint>)
|
|
||||||
{
|
|
||||||
const char* op = c.mode == RefineConstraint::Union ? "union" : "intersect";
|
|
||||||
return tos(c.resultType) + " ~ refine " + tos(c.type) + " " + op + " " + tos(c.discriminant);
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<T, SetOpConstraint>)
|
else if constexpr (std::is_same_v<T, SetOpConstraint>)
|
||||||
{
|
{
|
||||||
const char* op = c.mode == SetOpConstraint::Union ? " | " : " & ";
|
const char* op = c.mode == SetOpConstraint::Union ? " | " : " & ";
|
||||||
|
@ -106,7 +106,12 @@ public:
|
|||||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("thread"), std::nullopt, Location());
|
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("thread"), std::nullopt, Location());
|
||||||
case PrimitiveType::Buffer:
|
case PrimitiveType::Buffer:
|
||||||
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("buffer"), std::nullopt, Location());
|
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("buffer"), std::nullopt, Location());
|
||||||
|
case PrimitiveType::Function:
|
||||||
|
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("function"), std::nullopt, Location());
|
||||||
|
case PrimitiveType::Table:
|
||||||
|
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("table"), std::nullopt, Location());
|
||||||
default:
|
default:
|
||||||
|
LUAU_ASSERT(false); // this should be unreachable.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1603,8 +1603,8 @@ struct TypeChecker2
|
|||||||
visit(indexExpr->expr, ValueContext::RValue);
|
visit(indexExpr->expr, ValueContext::RValue);
|
||||||
visit(indexExpr->index, ValueContext::RValue);
|
visit(indexExpr->index, ValueContext::RValue);
|
||||||
|
|
||||||
TypeId exprType = lookupType(indexExpr->expr);
|
TypeId exprType = follow(lookupType(indexExpr->expr));
|
||||||
TypeId indexType = lookupType(indexExpr->index);
|
TypeId indexType = follow(lookupType(indexExpr->index));
|
||||||
|
|
||||||
if (auto tt = get<TableType>(exprType))
|
if (auto tt = get<TableType>(exprType))
|
||||||
{
|
{
|
||||||
|
@ -349,7 +349,8 @@ TypeFamilyReductionResult<TypeId> lenFamilyFn(const std::vector<TypeId>& typePar
|
|||||||
TypeId operandTy = follow(typeParams.at(0));
|
TypeId operandTy = follow(typeParams.at(0));
|
||||||
|
|
||||||
// check to see if the operand type is resolved enough, and wait to reduce if not
|
// check to see if the operand type is resolved enough, and wait to reduce if not
|
||||||
if (isPending(operandTy, ctx->solver))
|
// the use of `typeFromNormal` later necessitates blocking on local types.
|
||||||
|
if (isPending(operandTy, ctx->solver) || get<LocalType>(operandTy))
|
||||||
return {std::nullopt, false, {operandTy}, {}};
|
return {std::nullopt, false, {operandTy}, {}};
|
||||||
|
|
||||||
const NormalizedType* normTy = ctx->normalizer->normalize(operandTy);
|
const NormalizedType* normTy = ctx->normalizer->normalize(operandTy);
|
||||||
@ -964,6 +965,92 @@ TypeFamilyReductionResult<TypeId> eqFamilyFn(const std::vector<TypeId>& typePara
|
|||||||
return {ctx->builtins->booleanType, false, {}, {}};
|
return {ctx->builtins->booleanType, false, {}, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect types that prevent us from reducing a particular refinement.
|
||||||
|
struct FindRefinementBlockers : TypeOnceVisitor
|
||||||
|
{
|
||||||
|
DenseHashSet<TypeId> found{nullptr};
|
||||||
|
bool visit(TypeId ty, const BlockedType&) override
|
||||||
|
{
|
||||||
|
found.insert(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const PendingExpansionType&) override
|
||||||
|
{
|
||||||
|
found.insert(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const LocalType&) override
|
||||||
|
{
|
||||||
|
found.insert(ty);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(TypeId ty, const ClassType&) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TypeFamilyReductionResult<TypeId> refineFamilyFn(const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx)
|
||||||
|
{
|
||||||
|
if (typeParams.size() != 2 || !packParams.empty())
|
||||||
|
{
|
||||||
|
ctx->ice->ice("refine type family: encountered a type family instance without the required argument structure");
|
||||||
|
LUAU_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeId targetTy = follow(typeParams.at(0));
|
||||||
|
TypeId discriminantTy = follow(typeParams.at(1));
|
||||||
|
|
||||||
|
// check to see if both operand types are resolved enough, and wait to reduce if not
|
||||||
|
if (isPending(targetTy, ctx->solver))
|
||||||
|
return {std::nullopt, false, {targetTy}, {}};
|
||||||
|
else if (isPending(discriminantTy, ctx->solver))
|
||||||
|
return {std::nullopt, false, {discriminantTy}, {}};
|
||||||
|
|
||||||
|
// we need a more complex check for blocking on the discriminant in particular
|
||||||
|
FindRefinementBlockers frb;
|
||||||
|
frb.traverse(discriminantTy);
|
||||||
|
|
||||||
|
if (!frb.found.empty())
|
||||||
|
return {std::nullopt, false, {frb.found.begin(), frb.found.end()}, {}};
|
||||||
|
|
||||||
|
/* HACK: Refinements sometimes produce a type T & ~any under the assumption
|
||||||
|
* that ~any is the same as any. This is so so weird, but refinements needs
|
||||||
|
* some way to say "I may refine this, but I'm not sure."
|
||||||
|
*
|
||||||
|
* It does this by refining on a blocked type and deferring the decision
|
||||||
|
* until it is unblocked.
|
||||||
|
*
|
||||||
|
* Refinements also get negated, so we wind up with types like T & ~*blocked*
|
||||||
|
*
|
||||||
|
* We need to treat T & ~any as T in this case.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (auto nt = get<NegationType>(discriminantTy))
|
||||||
|
if (get<AnyType>(follow(nt->ty)))
|
||||||
|
return {targetTy, false, {}, {}};
|
||||||
|
|
||||||
|
TypeId intersection = ctx->arena->addType(IntersectionType{{targetTy, discriminantTy}});
|
||||||
|
const NormalizedType* normIntersection = ctx->normalizer->normalize(intersection);
|
||||||
|
const NormalizedType* normType = ctx->normalizer->normalize(targetTy);
|
||||||
|
|
||||||
|
// if the intersection failed to normalize, we can't reduce, but know nothing about inhabitance.
|
||||||
|
if (!normIntersection || !normType)
|
||||||
|
return {std::nullopt, false, {}, {}};
|
||||||
|
|
||||||
|
TypeId resultTy = ctx->normalizer->typeFromNormal(*normIntersection);
|
||||||
|
|
||||||
|
// include the error type if the target type is error-suppressing and the intersection we computed is not
|
||||||
|
if (normType->shouldSuppressErrors() && !normIntersection->shouldSuppressErrors())
|
||||||
|
resultTy = ctx->arena->addType(UnionType{{resultTy, ctx->builtins->errorType}});
|
||||||
|
|
||||||
|
return {resultTy, false, {}, {}};
|
||||||
|
}
|
||||||
|
|
||||||
BuiltinTypeFamilies::BuiltinTypeFamilies()
|
BuiltinTypeFamilies::BuiltinTypeFamilies()
|
||||||
: notFamily{"not", notFamilyFn}
|
: notFamily{"not", notFamilyFn}
|
||||||
, lenFamily{"len", lenFamilyFn}
|
, lenFamily{"len", lenFamilyFn}
|
||||||
@ -981,6 +1068,7 @@ BuiltinTypeFamilies::BuiltinTypeFamilies()
|
|||||||
, ltFamily{"lt", ltFamilyFn}
|
, ltFamily{"lt", ltFamilyFn}
|
||||||
, leFamily{"le", leFamilyFn}
|
, leFamily{"le", leFamilyFn}
|
||||||
, eqFamily{"eq", eqFamilyFn}
|
, eqFamily{"eq", eqFamilyFn}
|
||||||
|
, refineFamily{"refine", refineFamilyFn}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ int main(int argc, char** argv)
|
|||||||
else if (strncmp(argv[i], "--fflags=", 9) == 0)
|
else if (strncmp(argv[i], "--fflags=", 9) == 0)
|
||||||
setLuauFlags(argv[i] + 9);
|
setLuauFlags(argv[i] + 9);
|
||||||
else if (strncmp(argv[i], "-j", 2) == 0)
|
else if (strncmp(argv[i], "-j", 2) == 0)
|
||||||
threadCount = strtol(argv[i] + 2, nullptr, 10);
|
threadCount = int(strtol(argv[i] + 2, nullptr, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(LUAU_ENABLE_TIME_TRACE)
|
#if !defined(LUAU_ENABLE_TIME_TRACE)
|
||||||
@ -363,6 +363,7 @@ int main(int argc, char** argv)
|
|||||||
if (threadCount <= 0)
|
if (threadCount <= 0)
|
||||||
threadCount = std::min(TaskScheduler::getThreadCount(), 8u);
|
threadCount = std::min(TaskScheduler::getThreadCount(), 8u);
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
TaskScheduler scheduler(threadCount);
|
TaskScheduler scheduler(threadCount);
|
||||||
|
|
||||||
@ -370,6 +371,19 @@ int main(int argc, char** argv)
|
|||||||
scheduler.push(std::move(f));
|
scheduler.push(std::move(f));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
catch (const Luau::InternalCompilerError& ice)
|
||||||
|
{
|
||||||
|
Luau::Location location = ice.location ? *ice.location : Luau::Location();
|
||||||
|
|
||||||
|
std::string moduleName = ice.moduleName ? *ice.moduleName : "<unknown module>";
|
||||||
|
std::string humanReadableName = frontend.fileResolver->getHumanReadableModuleName(moduleName);
|
||||||
|
|
||||||
|
Luau::TypeError error(location, moduleName, Luau::InternalError{ice.message});
|
||||||
|
|
||||||
|
report(format, humanReadableName.c_str(), location, "InternalCompilerError",
|
||||||
|
Luau::toString(error, Luau::TypeErrorToStringOptions{frontend.fileResolver}).c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int failed = 0;
|
int failed = 0;
|
||||||
|
|
||||||
|
@ -22,16 +22,9 @@ RequireResolver::RequireResolver(lua_State* L, std::string path)
|
|||||||
if (isAbsolutePath(pathToResolve))
|
if (isAbsolutePath(pathToResolve))
|
||||||
luaL_argerrorL(L, 1, "cannot require an absolute path");
|
luaL_argerrorL(L, 1, "cannot require an absolute path");
|
||||||
|
|
||||||
bool isAlias = !pathToResolve.empty() && pathToResolve[0] == '@';
|
|
||||||
if (!isAlias && !isExplicitlyRelative(pathToResolve))
|
|
||||||
luaL_argerrorL(L, 1, "must require an alias prepended with '@' or an explicitly relative path");
|
|
||||||
|
|
||||||
std::replace(pathToResolve.begin(), pathToResolve.end(), '\\', '/');
|
std::replace(pathToResolve.begin(), pathToResolve.end(), '\\', '/');
|
||||||
if (isAlias)
|
|
||||||
{
|
substituteAliasIfPresent(pathToResolve);
|
||||||
pathToResolve = pathToResolve.substr(1);
|
|
||||||
substituteAliasIfPresent(pathToResolve);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] RequireResolver::ResolvedRequire RequireResolver::resolveRequire(lua_State* L, std::string path)
|
[[nodiscard]] RequireResolver::ResolvedRequire RequireResolver::resolveRequire(lua_State* L, std::string path)
|
||||||
@ -209,16 +202,22 @@ std::string RequireResolver::getRequiringContextRelative()
|
|||||||
|
|
||||||
void RequireResolver::substituteAliasIfPresent(std::string& path)
|
void RequireResolver::substituteAliasIfPresent(std::string& path)
|
||||||
{
|
{
|
||||||
std::string potentialAlias = path.substr(0, path.find_first_of("\\/"));
|
if (path.size() < 1 || path[0] != '@')
|
||||||
|
return;
|
||||||
|
std::string potentialAlias = path.substr(1, path.find_first_of("\\/"));
|
||||||
|
|
||||||
// Not worth searching when potentialAlias cannot be an alias
|
// Not worth searching when potentialAlias cannot be an alias
|
||||||
if (!Luau::isValidAlias(potentialAlias))
|
if (!Luau::isValidAlias(potentialAlias))
|
||||||
return;
|
luaL_errorL(L, "@%s is not a valid alias", potentialAlias.c_str());
|
||||||
|
|
||||||
std::optional<std::string> alias = getAlias(potentialAlias);
|
std::optional<std::string> alias = getAlias(potentialAlias);
|
||||||
if (alias)
|
if (alias)
|
||||||
{
|
{
|
||||||
path = *alias + path.substr(potentialAlias.size());
|
path = *alias + path.substr(potentialAlias.size() + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaL_errorL(L, "@%s is not a valid alias", potentialAlias.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@ -285,9 +286,8 @@ public:
|
|||||||
using value_type = Item;
|
using value_type = Item;
|
||||||
using reference = Item&;
|
using reference = Item&;
|
||||||
using pointer = Item*;
|
using pointer = Item*;
|
||||||
using iterator = pointer;
|
using difference_type = ptrdiff_t;
|
||||||
using difference_type = size_t;
|
using iterator_category = std::forward_iterator_tag;
|
||||||
using iterator_category = std::input_iterator_tag;
|
|
||||||
|
|
||||||
const_iterator()
|
const_iterator()
|
||||||
: set(0)
|
: set(0)
|
||||||
@ -348,6 +348,12 @@ public:
|
|||||||
class iterator
|
class iterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using value_type = MutableItem;
|
||||||
|
using reference = MutableItem&;
|
||||||
|
using pointer = MutableItem*;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
iterator()
|
iterator()
|
||||||
: set(0)
|
: set(0)
|
||||||
, index(0)
|
, index(0)
|
||||||
|
@ -438,7 +438,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass")
|
|||||||
REQUIRE(2 == root->body.size);
|
REQUIRE(2 == root->body.size);
|
||||||
|
|
||||||
std::string_view expected1 =
|
std::string_view expected1 =
|
||||||
R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]}},{"name":"method","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,21 - 4,11","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}}}],"indexer":null})";
|
R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]}},{"name":"method","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,21 - 4,11","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}}}],"indexer":null})";
|
||||||
CHECK(toJson(root->body.data[0]) == expected1);
|
CHECK(toJson(root->body.data[0]) == expected1);
|
||||||
|
|
||||||
std::string_view expected2 =
|
std::string_view expected2 =
|
||||||
@ -451,7 +451,39 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation")
|
|||||||
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())");
|
||||||
|
|
||||||
std::string_view expected =
|
std::string_view expected =
|
||||||
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"type":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeFunction","location":"0,10 - 0,36","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"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","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"returnTypes":{"type":"AstTypeList","types":[]}}]},"exported":false})";
|
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,55","name":"T","generics":[],"genericPacks":[],"type":{"type":"AstTypeIntersection","location":"0,9 - 0,55","types":[{"type":"AstTypeFunction","location":"0,10 - 0,36","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","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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_type_literal")
|
||||||
|
{
|
||||||
|
AstStat* statement = expectParseStatement(R"(type Action = { strings: "A" | "B" | "C", mixed: "This" | "That" | true })");
|
||||||
|
|
||||||
|
auto json = toJson(statement);
|
||||||
|
|
||||||
|
std::string_view expected =
|
||||||
|
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,73","name":"Action","generics":[],"genericPacks":[],"type":{"type":"AstTypeTable","location":"0,14 - 0,73","props":[{"name":"strings","type":"AstTableProp","location":"0,16 - 0,23","propType":{"type":"AstTypeUnion","location":"0,25 - 0,40","types":[{"type":"AstTypeSingletonString","location":"0,25 - 0,28","value":"A"},{"type":"AstTypeSingletonString","location":"0,31 - 0,34","value":"B"},{"type":"AstTypeSingletonString","location":"0,37 - 0,40","value":"C"}]}},{"name":"mixed","type":"AstTableProp","location":"0,42 - 0,47","propType":{"type":"AstTypeUnion","location":"0,49 - 0,71","types":[{"type":"AstTypeSingletonString","location":"0,49 - 0,55","value":"This"},{"type":"AstTypeSingletonString","location":"0,58 - 0,64","value":"That"},{"type":"AstTypeSingletonBool","location":"0,67 - 0,71","value":true}]}}],"indexer":null},"exported":false})";
|
||||||
|
|
||||||
|
CHECK(toJson(statement) == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_indexed_type_literal")
|
||||||
|
{
|
||||||
|
AstStat* statement = expectParseStatement(R"(type StringSet = { [string]: true })");
|
||||||
|
|
||||||
|
std::string_view expected =
|
||||||
|
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,35","name":"StringSet","generics":[],"genericPacks":[],"type":{"type":"AstTypeTable","location":"0,17 - 0,35","props":[],"indexer":{"location":"0,19 - 0,33","indexType":{"type":"AstTypeReference","location":"0,20 - 0,26","name":"string","nameLocation":"0,20 - 0,26","parameters":[]},"resultType":{"type":"AstTypeSingletonBool","location":"0,29 - 0,33","value":true}}},"exported":false})";
|
||||||
|
|
||||||
|
CHECK(toJson(statement) == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeFunction")
|
||||||
|
{
|
||||||
|
AstStat* statement = expectParseStatement(R"(type fun = (string, bool, named: number) -> ())");
|
||||||
|
|
||||||
|
std::string_view expected =
|
||||||
|
R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"type":{"type":"AstTypeFunction","location":"0,11 - 0,46","generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypeList","types":[]}},"exported":false})";
|
||||||
|
|
||||||
CHECK(toJson(statement) == expected);
|
CHECK(toJson(statement) == expected);
|
||||||
}
|
}
|
||||||
|
@ -78,10 +78,14 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "class_method")
|
|||||||
declare class Foo
|
declare class Foo
|
||||||
function bar(self, x: string): number
|
function bar(self, x: string): number
|
||||||
end
|
end
|
||||||
|
|
||||||
|
declare Foo: {
|
||||||
|
new: () -> Foo
|
||||||
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
std::optional<DocumentationSymbol> symbol = getDocSymbol(R"(
|
std::optional<DocumentationSymbol> symbol = getDocSymbol(R"(
|
||||||
local x: Foo
|
local x: Foo = Foo.new()
|
||||||
x:bar("asdf")
|
x:bar("asdf")
|
||||||
)",
|
)",
|
||||||
Position(2, 11));
|
Position(2, 11));
|
||||||
@ -96,10 +100,14 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "overloaded_class_method")
|
|||||||
function bar(self, x: string): number
|
function bar(self, x: string): number
|
||||||
function bar(self, x: number): string
|
function bar(self, x: number): string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
declare Foo: {
|
||||||
|
new: () -> Foo
|
||||||
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
std::optional<DocumentationSymbol> symbol = getDocSymbol(R"(
|
std::optional<DocumentationSymbol> symbol = getDocSymbol(R"(
|
||||||
local x: Foo
|
local x: Foo = Foo.new()
|
||||||
x:bar("asdf")
|
x:bar("asdf")
|
||||||
)",
|
)",
|
||||||
Position(2, 11));
|
Position(2, 11));
|
||||||
|
@ -3265,8 +3265,9 @@ end
|
|||||||
|
|
||||||
{
|
{
|
||||||
check(R"(
|
check(R"(
|
||||||
local t: Foo
|
local function f(t: Foo)
|
||||||
t:@1
|
t:@1
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
auto ac = autocomplete('1');
|
auto ac = autocomplete('1');
|
||||||
@ -3281,8 +3282,9 @@ t:@1
|
|||||||
|
|
||||||
{
|
{
|
||||||
check(R"(
|
check(R"(
|
||||||
local t: Foo
|
local function f(t: Foo)
|
||||||
t.@1
|
t.@1
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
auto ac = autocomplete('1');
|
auto ac = autocomplete('1');
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/DataFlowGraph.h"
|
#include "Luau/DataFlowGraph.h"
|
||||||
|
#include "Fixture.h"
|
||||||
#include "Luau/Error.h"
|
#include "Luau/Error.h"
|
||||||
#include "Luau/Parser.h"
|
#include "Luau/Parser.h"
|
||||||
|
|
||||||
@ -560,4 +561,74 @@ TEST_CASE_FIXTURE(DataFlowGraphFixture, "local_f_which_is_prototyped_enclosed_by
|
|||||||
CHECK(phi->operands.at(1) == f4);
|
CHECK(phi->operands.at(1) == f4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi_node_if_case_binding")
|
||||||
|
{
|
||||||
|
dfg(R"(
|
||||||
|
local x = nil
|
||||||
|
if true then
|
||||||
|
if true then
|
||||||
|
x = 5
|
||||||
|
end
|
||||||
|
print(x)
|
||||||
|
else
|
||||||
|
print(x)
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
DefId x1 = graph->getDef(query<AstStatLocal>(module)->vars.data[0]);
|
||||||
|
DefId x2 = getDef<AstExprLocal, 1>(); // x = 5
|
||||||
|
DefId x3 = getDef<AstExprLocal, 2>(); // print(x)
|
||||||
|
|
||||||
|
const Phi* phi = get<Phi>(x3);
|
||||||
|
REQUIRE(phi);
|
||||||
|
CHECK(phi->operands.at(0) == x2);
|
||||||
|
CHECK(phi->operands.at(1) == x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi_node_if_case_table_prop")
|
||||||
|
{
|
||||||
|
dfg(R"(
|
||||||
|
local t = {}
|
||||||
|
t.x = true
|
||||||
|
if true then
|
||||||
|
if true then
|
||||||
|
t.x = 5
|
||||||
|
end
|
||||||
|
print(t.x)
|
||||||
|
else
|
||||||
|
print(t.x)
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
DefId x1 = getDef<AstExprIndexName, 1>(); // t.x = true
|
||||||
|
DefId x2 = getDef<AstExprIndexName, 2>(); // t.x = 5
|
||||||
|
|
||||||
|
DefId x3 = getDef<AstExprIndexName, 3>(); // print(t.x)
|
||||||
|
const Phi* phi = get<Phi>(x3);
|
||||||
|
REQUIRE(phi);
|
||||||
|
CHECK(phi->operands.size() == 2);
|
||||||
|
CHECK(phi->operands.at(0) == x1);
|
||||||
|
CHECK(phi->operands.at(1) == x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(DataFlowGraphFixture, "phi_node_if_case_table_prop_literal")
|
||||||
|
{
|
||||||
|
dfg(R"(
|
||||||
|
local t = { x = true }
|
||||||
|
if true then
|
||||||
|
t.x = 5
|
||||||
|
end
|
||||||
|
print(t.x)
|
||||||
|
|
||||||
|
)");
|
||||||
|
|
||||||
|
DefId x1 = getDef<AstExprConstantBool, 1>(); // {x = true <- }
|
||||||
|
DefId x2 = getDef<AstExprIndexName, 1>(); // t.x = 5
|
||||||
|
DefId x3 = getDef<AstExprIndexName, 2>(); // print(t.x)
|
||||||
|
const Phi* phi = get<Phi>(x3);
|
||||||
|
REQUIRE(phi);
|
||||||
|
CHECK(phi->operands.size() == 2);
|
||||||
|
CHECK(phi->operands.at(0) == x1);
|
||||||
|
CHECK(phi->operands.at(1) == x2);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -81,12 +81,41 @@ declare function @checked abs(n: number): number
|
|||||||
declare function @checked lower(s: string): string
|
declare function @checked lower(s: string): string
|
||||||
declare function cond() : boolean
|
declare function cond() : boolean
|
||||||
declare function @checked contrived(n : Not<number>) : number
|
declare function @checked contrived(n : Not<number>) : number
|
||||||
|
|
||||||
|
-- interesting types of things that we would like to mark as checked
|
||||||
|
declare function @checked onlyNums(...: number) : number
|
||||||
|
declare function @checked mixedArgs(x: string, ...: number) : number
|
||||||
|
declare function @checked optionalArg(x: string?) : number
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("NonStrictTypeCheckerTest");
|
TEST_SUITE_BEGIN("NonStrictTypeCheckerTest");
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "interesting_checked_functions")
|
||||||
|
{
|
||||||
|
CheckResult result = checkNonStrict(R"(
|
||||||
|
onlyNums(1,1,1)
|
||||||
|
onlyNums(1, "a")
|
||||||
|
|
||||||
|
mixedArgs("a", 1, 2)
|
||||||
|
mixedArgs(1, 1, 1)
|
||||||
|
mixedArgs("a", true)
|
||||||
|
|
||||||
|
optionalArg(nil)
|
||||||
|
optionalArg("a")
|
||||||
|
optionalArg(3)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 12), "onlyNums", result); // onlyNums(1, "a")
|
||||||
|
|
||||||
|
NONSTRICT_REQUIRE_CHECKED_ERR(Position(5, 10), "mixedArgs", result); // mixedArgs(1, 1, 1)
|
||||||
|
NONSTRICT_REQUIRE_CHECKED_ERR(Position(6, 15), "mixedArgs", result); // mixedArgs("a", true)
|
||||||
|
|
||||||
|
NONSTRICT_REQUIRE_CHECKED_ERR(Position(10, 12), "optionalArg", result); // optionalArg(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "simple_negation_caching_example")
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "simple_negation_caching_example")
|
||||||
{
|
{
|
||||||
CheckResult result = checkNonStrict(R"(
|
CheckResult result = checkNonStrict(R"(
|
||||||
@ -387,4 +416,35 @@ lower(x)
|
|||||||
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 6), "lower", result);
|
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 6), "lower", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "phi_node_assignment")
|
||||||
|
{
|
||||||
|
CheckResult result = checkNonStrict(R"(
|
||||||
|
local x = "a" -- x1
|
||||||
|
if cond() then
|
||||||
|
x = 3 -- x2
|
||||||
|
end
|
||||||
|
lower(x) -- phi {x1, x2}
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "phi_node_assignment_err")
|
||||||
|
{
|
||||||
|
CheckResult result = checkNonStrict(R"(
|
||||||
|
local x = nil
|
||||||
|
if cond() then
|
||||||
|
if cond() then
|
||||||
|
x = 5
|
||||||
|
end
|
||||||
|
abs(x)
|
||||||
|
else
|
||||||
|
lower(x)
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
NONSTRICT_REQUIRE_CHECKED_ERR(Position(8, 10), "lower", result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -394,4 +394,39 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "RequirePathWithParentAlias")
|
|||||||
assertOutputContainsAll({"true", "result from other_dependency"});
|
assertOutputContainsAll({"true", "result from other_dependency"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "RequireAliasThatDoesNotExist")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauUpdatedRequireByStringSemantics, true};
|
||||||
|
std::string nonExistentAlias = "@this.alias.does.not.exist";
|
||||||
|
|
||||||
|
runProtectedRequire(nonExistentAlias);
|
||||||
|
assertOutputContainsAll({"false", "@this.alias.does.not.exist is not a valid alias"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ReplWithPathFixture, "AliasHasIllegalFormat")
|
||||||
|
{
|
||||||
|
ScopedFastFlag sff{FFlag::LuauUpdatedRequireByStringSemantics, true};
|
||||||
|
std::string illegalCharacter = "@@";
|
||||||
|
|
||||||
|
runProtectedRequire(illegalCharacter);
|
||||||
|
assertOutputContainsAll({"false", "@@ is not a valid alias"});
|
||||||
|
|
||||||
|
std::string pathAlias1 = "@.";
|
||||||
|
|
||||||
|
runProtectedRequire(pathAlias1);
|
||||||
|
assertOutputContainsAll({"false", ". is not a valid alias"});
|
||||||
|
|
||||||
|
|
||||||
|
std::string pathAlias2 = "@..";
|
||||||
|
|
||||||
|
runProtectedRequire(pathAlias2);
|
||||||
|
assertOutputContainsAll({"false", ".. is not a valid alias"});
|
||||||
|
|
||||||
|
std::string emptyAlias = "@";
|
||||||
|
|
||||||
|
runProtectedRequire(emptyAlias);
|
||||||
|
assertOutputContainsAll({"false", " is not a valid alias"});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -938,6 +938,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tonumber_returns_optional_number_type2")
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "dont_add_definitions_to_persistent_types")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "dont_add_definitions_to_persistent_types")
|
||||||
{
|
{
|
||||||
|
// This test makes no sense with type states and I think it generally makes no sense under the new solver.
|
||||||
|
// TODO: clip.
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
return;
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local f = math.sin
|
local f = math.sin
|
||||||
local function g(x) return math.sin(x) end
|
local function g(x) return math.sin(x) end
|
||||||
@ -1093,7 +1098,7 @@ end
|
|||||||
TEST_CASE_FIXTURE(Fixture, "string_match")
|
TEST_CASE_FIXTURE(Fixture, "string_match")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local s:string
|
local s: string = "hello"
|
||||||
local p = s:match("foo")
|
local p = s:match("foo")
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
@ -230,10 +230,14 @@ TEST_CASE_FIXTURE(Fixture, "class_definition_function_prop")
|
|||||||
declare class Foo
|
declare class Foo
|
||||||
X: (number) -> string
|
X: (number) -> string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
declare Foo: {
|
||||||
|
new: () -> Foo
|
||||||
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x: Foo
|
local x: Foo = Foo.new()
|
||||||
local prop = x.X
|
local prop = x.X
|
||||||
)");
|
)");
|
||||||
|
|
||||||
@ -250,10 +254,14 @@ TEST_CASE_FIXTURE(Fixture, "definition_file_class_function_args")
|
|||||||
|
|
||||||
y: (a: number, b: string) -> string
|
y: (a: number, b: string) -> string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
declare Foo: {
|
||||||
|
new: () -> Foo
|
||||||
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x: Foo
|
local x: Foo = Foo.new()
|
||||||
local methodRef1 = x.foo1
|
local methodRef1 = x.foo1
|
||||||
local methodRef2 = x.foo2
|
local methodRef2 = x.foo2
|
||||||
local prop = x.y
|
local prop = x.y
|
||||||
|
@ -2183,4 +2183,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "apply_of_lambda_with_inferred_and_explicit_t
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "regex_benchmark_string_format_minimization")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
(nil :: any)(function(n)
|
||||||
|
if tonumber(n) then
|
||||||
|
n = tonumber(n)
|
||||||
|
elseif n ~= nil then
|
||||||
|
string.format("invalid argument #4 to 'sub': number expected, got %s", typeof(n))
|
||||||
|
end
|
||||||
|
end);
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -304,8 +304,15 @@ TEST_CASE_FIXTURE(Fixture, "calling_self_generic_methods")
|
|||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
// TODO: Should typecheck but currently errors CLI-54277
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
LUAU_REQUIRE_ERRORS(result);
|
{
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
|
||||||
|
CHECK_EQ("{ f: (t1) -> (), id: <a>(unknown, a) -> a } where t1 = { id: ((t1, number) -> number) & ((t1, string) -> string) }",
|
||||||
|
toString(requireType("x"), {true}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LUAU_REQUIRE_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "infer_generic_property")
|
TEST_CASE_FIXTURE(Fixture, "infer_generic_property")
|
||||||
@ -692,7 +699,7 @@ local d: D = c
|
|||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_functions_dont_cache_type_parameters")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "generic_functions_dont_cache_type_parameters")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
-- See https://github.com/Roblox/luau/issues/332
|
-- See https://github.com/luau-lang/luau/issues/332
|
||||||
-- This function has a type parameter with the same name as clones,
|
-- This function has a type parameter with the same name as clones,
|
||||||
-- so if we cache type parameter names for functions these get confused.
|
-- so if we cache type parameter names for functions these get confused.
|
||||||
-- function id<Z>(x : Z) : Z
|
-- function id<Z>(x : Z) : Z
|
||||||
@ -1147,7 +1154,7 @@ TEST_CASE_FIXTURE(Fixture, "substitution_with_bound_table")
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics1")
|
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics1")
|
||||||
{
|
{
|
||||||
// https://github.com/Roblox/luau/issues/484
|
// https://github.com/luau-lang/luau/issues/484
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
type MyObject = {
|
type MyObject = {
|
||||||
@ -1175,7 +1182,7 @@ local complex: ComplexObject<string> = {
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics2")
|
TEST_CASE_FIXTURE(Fixture, "apply_type_function_nested_generics2")
|
||||||
{
|
{
|
||||||
// https://github.com/Roblox/luau/issues/484
|
// https://github.com/luau-lang/luau/issues/484
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
type MyObject = {
|
type MyObject = {
|
||||||
@ -1186,15 +1193,15 @@ type ComplexObject<T> = {
|
|||||||
nested: MyObject
|
nested: MyObject
|
||||||
}
|
}
|
||||||
|
|
||||||
local complex2: ComplexObject<string> = nil
|
function f(complex: ComplexObject<string>)
|
||||||
|
local x = complex.nested.getReturnValue(function(): string
|
||||||
|
return ""
|
||||||
|
end)
|
||||||
|
|
||||||
local x = complex2.nested.getReturnValue(function(): string
|
local y = complex.nested.getReturnValue(function()
|
||||||
return ""
|
return 3
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
local y = complex2.nested.getReturnValue(function()
|
|
||||||
return 3
|
|
||||||
end)
|
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
@ -1855,7 +1855,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_annotations_arent_relevant_when_doing_d
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "function_call_with_colon_after_refining_not_to_be_nil")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "function_call_with_colon_after_refining_not_to_be_nil")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true};
|
// don't run this test at all without DCR
|
||||||
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
return;
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
--!strict
|
--!strict
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "Fixture.h"
|
#include "Fixture.h"
|
||||||
|
|
||||||
#include "doctest.h"
|
#include "doctest.h"
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
@ -141,9 +140,9 @@ TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons_mismatch")
|
TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons_mismatch")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f(a, b) end
|
function f(g: ((true, string) -> ()) & ((false, number) -> ()))
|
||||||
local g : ((true, string) -> ()) & ((false, number) -> ()) = (f::any)
|
g(true, 37)
|
||||||
g(true, 37)
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||||
@ -429,7 +428,11 @@ TEST_CASE_FIXTURE(Fixture, "widening_happens_almost_everywhere")
|
|||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ("string", toString(requireType("copy")));
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK_EQ(R"("foo")", toString(requireType("copy")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("string", toString(requireType("copy")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "widening_happens_almost_everywhere_except_for_tables")
|
TEST_CASE_FIXTURE(Fixture, "widening_happens_almost_everywhere_except_for_tables")
|
||||||
|
@ -292,6 +292,7 @@ TEST_CASE_FIXTURE(TypeStateFixture, "invalidate_type_refinements_upon_assignment
|
|||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
TEST_CASE_FIXTURE(TypeStateFixture, "local_t_is_assigned_a_fresh_table_with_x_assigned_a_union_and_then_assert_restricts_actual_outflow_of_types")
|
TEST_CASE_FIXTURE(TypeStateFixture, "local_t_is_assigned_a_fresh_table_with_x_assigned_a_union_and_then_assert_restricts_actual_outflow_of_types")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
@ -314,6 +315,7 @@ TEST_CASE_FIXTURE(TypeStateFixture, "local_t_is_assigned_a_fresh_table_with_x_as
|
|||||||
// CHECK("boolean | string" == toString(requireType("x")));
|
// CHECK("boolean | string" == toString(requireType("x")));
|
||||||
CHECK("boolean | number | number | string" == toString(requireType("x")));
|
CHECK("boolean | number | number | string" == toString(requireType("x")));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_are_unions_of_all_assignments")
|
TEST_CASE_FIXTURE(TypeStateFixture, "captured_locals_are_unions_of_all_assignments")
|
||||||
{
|
{
|
||||||
@ -399,4 +401,21 @@ TEST_CASE_FIXTURE(TypeStateFixture, "prototyped_recursive_functions_but_has_prev
|
|||||||
CHECK("((() -> ()) | number)?" == toString(requireType("f")));
|
CHECK("((() -> ()) | number)?" == toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(TypeStateFixture, "multiple_assignments_in_loops")
|
||||||
|
{
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local x = nil
|
||||||
|
|
||||||
|
for i = 1, 10 do
|
||||||
|
x = 5
|
||||||
|
x = "hello"
|
||||||
|
end
|
||||||
|
|
||||||
|
print(x)
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
CHECK("(number | string)?" == toString(requireType("x")));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
@ -45,8 +45,9 @@ TEST_CASE_FIXTURE(Fixture, "allow_specific_assign")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "allow_more_specific_assign")
|
TEST_CASE_FIXTURE(Fixture, "allow_more_specific_assign")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a:number|string = 22
|
function f(a: number | string, b: (number | string)?)
|
||||||
local b:number|string|nil = a
|
b = a
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
@ -55,25 +56,14 @@ TEST_CASE_FIXTURE(Fixture, "allow_more_specific_assign")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "disallow_less_specific_assign")
|
TEST_CASE_FIXTURE(Fixture, "disallow_less_specific_assign")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a:number = 10
|
function f(a: number, b: number | string)
|
||||||
local b:number|string = 20
|
a = b
|
||||||
a = b
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "disallow_less_specific_assign2")
|
|
||||||
{
|
|
||||||
CheckResult result = check(R"(
|
|
||||||
local a:number? = 10
|
|
||||||
local b:number|string? = 20
|
|
||||||
a = b
|
|
||||||
)");
|
|
||||||
|
|
||||||
REQUIRE_EQ(1, result.errors.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "optional_arguments")
|
TEST_CASE_FIXTURE(Fixture, "optional_arguments")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
@ -128,13 +118,14 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_property_guaranteed_to_ex
|
|||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = {x: number}
|
type A = {x: number}
|
||||||
type B = {x: number}
|
type B = {x: number}
|
||||||
local t: A | B
|
|
||||||
|
|
||||||
local r = t.x
|
function f(t: A | B)
|
||||||
|
return t.x
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ(*builtinTypes->numberType, *requireType("r"));
|
CHECK_EQ("(A | B) -> number", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_mixed_types")
|
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_mixed_types")
|
||||||
@ -142,13 +133,14 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_mixed_types")
|
|||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = {x: number}
|
type A = {x: number}
|
||||||
type B = {x: string}
|
type B = {x: string}
|
||||||
local t: A | B
|
|
||||||
|
|
||||||
local r = t.x
|
function f(t: A | B)
|
||||||
|
return t.x
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ("number | string", toString(requireType("r")));
|
CHECK_EQ("(A | B) -> number | string", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_works_at_arbitrary_depth")
|
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_works_at_arbitrary_depth")
|
||||||
@ -156,13 +148,14 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_works_at_arbitrary_depth")
|
|||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = {x: {y: {z: {thing: number}}}}
|
type A = {x: {y: {z: {thing: number}}}}
|
||||||
type B = {x: {y: {z: {thing: string}}}}
|
type B = {x: {y: {z: {thing: string}}}}
|
||||||
local t: A | B
|
|
||||||
|
|
||||||
local r = t.x.y.z.thing
|
function f(t: A | B)
|
||||||
|
return t.x.y.z.thing
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ("number | string", toString(requireType("r")));
|
CHECK_EQ("(A | B) -> number | string", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_optional_property")
|
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_optional_property")
|
||||||
@ -170,13 +163,14 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_optional_property")
|
|||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = {x: number}
|
type A = {x: number}
|
||||||
type B = {x: number?}
|
type B = {x: number?}
|
||||||
local t: A | B
|
|
||||||
|
|
||||||
local r = t.x
|
function f(t: A | B)
|
||||||
|
return t.x
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ("number?", toString(requireType("r")));
|
CHECK_EQ("(A | B) -> number?", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_missing_property")
|
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_missing_property")
|
||||||
@ -184,22 +178,22 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_missing_property")
|
|||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = {x: number}
|
type A = {x: number}
|
||||||
type B = {}
|
type B = {}
|
||||||
local t: A | B
|
|
||||||
|
|
||||||
local r = t.x
|
function f(t: A | B)
|
||||||
|
return t.x
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
|
||||||
MissingUnionProperty* mup = get<MissingUnionProperty>(result.errors[0]);
|
MissingUnionProperty* mup = get<MissingUnionProperty>(result.errors[0]);
|
||||||
REQUIRE(mup);
|
REQUIRE(mup);
|
||||||
CHECK_EQ(mup->type, requireType("t"));
|
CHECK_EQ("Key 'x' is missing from 'B' in the type 'A | B'", toString(result.errors[0]));
|
||||||
REQUIRE(mup->missing.size() == 1);
|
|
||||||
std::optional<TypeId> bTy = lookupType("B");
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
REQUIRE(bTy);
|
CHECK_EQ("(A | B) -> number | *error-type*", toString(requireType("f")));
|
||||||
CHECK_EQ(mup->missing[0], *bTy);
|
else
|
||||||
CHECK_EQ(mup->key, "x");
|
CHECK_EQ("(A | B) -> *error-type*", toString(requireType("f")));
|
||||||
CHECK_EQ("*error-type*", toString(requireType("r")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_property_of_type_any")
|
TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_property_of_type_any")
|
||||||
@ -207,13 +201,14 @@ TEST_CASE_FIXTURE(Fixture, "index_on_a_union_type_with_one_property_of_type_any"
|
|||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = {x: number}
|
type A = {x: number}
|
||||||
type B = {x: any}
|
type B = {x: any}
|
||||||
local t: A | B
|
|
||||||
|
|
||||||
local r = t.x
|
function f(t: A | B)
|
||||||
|
return t.x
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
CHECK_EQ(*builtinTypes->anyType, *requireType("r"));
|
CHECK_EQ("(A | B) -> any", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "union_equality_comparisons")
|
TEST_CASE_FIXTURE(Fixture, "union_equality_comparisons")
|
||||||
@ -223,14 +218,13 @@ TEST_CASE_FIXTURE(Fixture, "union_equality_comparisons")
|
|||||||
type B = number | nil
|
type B = number | nil
|
||||||
type C = number | boolean
|
type C = number | boolean
|
||||||
|
|
||||||
local a = 1 :: A
|
function f(a: A, b: B, c: C)
|
||||||
local b = nil :: B
|
local n = 1
|
||||||
local c = true :: C
|
|
||||||
local n = 1
|
|
||||||
|
|
||||||
local x = a == b
|
local x = a == b
|
||||||
local y = a == n
|
local y = a == n
|
||||||
local z = a == c
|
local z = a == c
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
@ -239,16 +233,21 @@ TEST_CASE_FIXTURE(Fixture, "union_equality_comparisons")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "optional_union_members")
|
TEST_CASE_FIXTURE(Fixture, "optional_union_members")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a = { a = { x = 1, y = 2 }, b = 3 }
|
local a = { a = { x = 1, y = 2 }, b = 3 }
|
||||||
type A = typeof(a)
|
type A = typeof(a)
|
||||||
local b: A? = a
|
function f(b: A?)
|
||||||
local bf = b
|
return b.a.y
|
||||||
local c = bf.a.y
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK_EQ(*builtinTypes->numberType, *requireType("c"));
|
|
||||||
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK_EQ("(A?) -> number | *error-type*", toString(requireType("f")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("(A?) -> number", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "optional_union_functions")
|
TEST_CASE_FIXTURE(Fixture, "optional_union_functions")
|
||||||
@ -257,37 +256,49 @@ TEST_CASE_FIXTURE(Fixture, "optional_union_functions")
|
|||||||
local a = {}
|
local a = {}
|
||||||
function a.foo(x:number, y:number) return x + y end
|
function a.foo(x:number, y:number) return x + y end
|
||||||
type A = typeof(a)
|
type A = typeof(a)
|
||||||
local b: A? = a
|
function f(b: A?)
|
||||||
local c = b.foo(1, 2)
|
return b.foo(1, 2)
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK_EQ(*builtinTypes->numberType, *requireType("c"));
|
|
||||||
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK_EQ("(A?) -> number | *error-type*", toString(requireType("f")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("(A?) -> number", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "optional_union_methods")
|
TEST_CASE_FIXTURE(Fixture, "optional_union_methods")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a = {}
|
local a = {}
|
||||||
function a:foo(x:number, y:number) return x + y end
|
function a:foo(x:number, y:number) return x + y end
|
||||||
type A = typeof(a)
|
type A = typeof(a)
|
||||||
local b: A? = a
|
function f(b: A?)
|
||||||
local c = b:foo(1, 2)
|
return b:foo(1, 2)
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK_EQ(*builtinTypes->numberType, *requireType("c"));
|
|
||||||
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
||||||
|
|
||||||
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
CHECK_EQ("(A?) -> number | *error-type*", toString(requireType("f")));
|
||||||
|
else
|
||||||
|
CHECK_EQ("(A?) -> number", toString(requireType("f")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "optional_union_follow")
|
TEST_CASE_FIXTURE(Fixture, "optional_union_follow")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local y: number? = 2
|
local y: number? = 2
|
||||||
local x = y
|
local x = y
|
||||||
local function f(a: number, b: typeof(x), c: typeof(x)) return -a end
|
function f(a: number, b: number?, c: number?) return -a end
|
||||||
return f()
|
return f()
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -302,10 +313,11 @@ return f()
|
|||||||
TEST_CASE_FIXTURE(Fixture, "optional_field_access_error")
|
TEST_CASE_FIXTURE(Fixture, "optional_field_access_error")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = { x: number }
|
type A = { x: number }
|
||||||
local b: A? = { x = 2 }
|
function f(b: A?)
|
||||||
local c = b.x
|
local c = b.x
|
||||||
local d = b.y
|
local d = b.y
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
LUAU_REQUIRE_ERROR_COUNT(3, result);
|
||||||
@ -317,9 +329,10 @@ local d = b.y
|
|||||||
TEST_CASE_FIXTURE(Fixture, "optional_index_error")
|
TEST_CASE_FIXTURE(Fixture, "optional_index_error")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = {number}
|
type A = {number}
|
||||||
local a: A? = {1, 2, 3}
|
function f(a: A?)
|
||||||
local b = a[1]
|
local b = a[1]
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -329,9 +342,10 @@ local b = a[1]
|
|||||||
TEST_CASE_FIXTURE(Fixture, "optional_call_error")
|
TEST_CASE_FIXTURE(Fixture, "optional_call_error")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = (number) -> number
|
type A = (number) -> number
|
||||||
local a: A? = function(a) return -a end
|
function f(a: A?)
|
||||||
local b = a(4)
|
local b = a(4)
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -341,18 +355,23 @@ local b = a(4)
|
|||||||
TEST_CASE_FIXTURE(Fixture, "optional_assignment_errors")
|
TEST_CASE_FIXTURE(Fixture, "optional_assignment_errors")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = { x: number }
|
type A = { x: number }
|
||||||
local a: A? = { x = 2 }
|
function f(a: A?)
|
||||||
a.x = 2
|
a.x = 2
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
CHECK_EQ("Value of type 'A?' could be nil", toString(result.errors[0]));
|
||||||
|
}
|
||||||
|
|
||||||
result = check(R"(
|
TEST_CASE_FIXTURE(Fixture, "optional_assignment_errors_2")
|
||||||
type A = { x: number } & { y: number }
|
{
|
||||||
local a: A? = { x = 2, y = 3 }
|
CheckResult result = check(R"(
|
||||||
a.x = 2
|
type A = { x: number } & { y: number }
|
||||||
|
function f(a: A?)
|
||||||
|
a.x = 2
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -366,9 +385,10 @@ a.x = 2
|
|||||||
TEST_CASE_FIXTURE(Fixture, "optional_length_error")
|
TEST_CASE_FIXTURE(Fixture, "optional_length_error")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = {number}
|
type A = {number}
|
||||||
local a: A? = {1, 2, 3}
|
function f(a: A?)
|
||||||
local b = #a
|
local b = #a
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -378,27 +398,27 @@ local b = #a
|
|||||||
TEST_CASE_FIXTURE(Fixture, "optional_missing_key_error_details")
|
TEST_CASE_FIXTURE(Fixture, "optional_missing_key_error_details")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = { x: number, y: number }
|
type A = { x: number, y: number }
|
||||||
type B = { x: number, y: number }
|
type B = { x: number, y: number }
|
||||||
type C = { x: number }
|
type C = { x: number }
|
||||||
type D = { x: number }
|
type D = { x: number }
|
||||||
|
|
||||||
local a: A|B|C|D
|
function f(a: A | B | C | D)
|
||||||
local b = a.y
|
local y = a.y
|
||||||
|
local z = a.z
|
||||||
|
end
|
||||||
|
|
||||||
local c: A|(B|C)?|D
|
function g(c: A | B | C | D | nil)
|
||||||
local d = c.y
|
local d = c.y
|
||||||
|
end
|
||||||
local e = a.z
|
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
CHECK_EQ("Key 'y' is missing from 'C', 'D' in the type 'A | B | C | D'", toString(result.errors[0]));
|
CHECK_EQ("Key 'y' is missing from 'C', 'D' in the type 'A | B | C | D'", toString(result.errors[0]));
|
||||||
|
CHECK_EQ("Type 'A | B | C | D' does not have key 'z'", toString(result.errors[1]));
|
||||||
|
|
||||||
CHECK_EQ("Value of type '(A | B | C | D)?' could be nil", toString(result.errors[1]));
|
CHECK_EQ("Value of type '(A | B | C | D)?' could be nil", toString(result.errors[2]));
|
||||||
CHECK_EQ("Key 'y' is missing from 'C', 'D' in the type 'A | B | C | D'", toString(result.errors[2]));
|
CHECK_EQ("Key 'y' is missing from 'C', 'D' in the type 'A | B | C | D'", toString(result.errors[3]));
|
||||||
|
|
||||||
CHECK_EQ("Type 'A | B | C | D' does not have key 'z'", toString(result.errors[3]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "optional_iteration")
|
TEST_CASE_FIXTURE(Fixture, "optional_iteration")
|
||||||
@ -470,25 +490,27 @@ type Z = { z: number }
|
|||||||
|
|
||||||
type XYZ = X | Y | Z
|
type XYZ = X | Y | Z
|
||||||
|
|
||||||
local a: XYZ
|
function f(a: XYZ)
|
||||||
local b: { w: number } = a
|
local b: { w: number } = a
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
TypeMismatch* tm = get<TypeMismatch>(result.errors[0]);
|
|
||||||
REQUIRE(tm);
|
|
||||||
|
|
||||||
CHECK_EQ(tm->reason, "Not all union options are compatible.");
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
||||||
|
{
|
||||||
CHECK_EQ("X | Y | Z", toString(tm->givenType));
|
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"
|
||||||
const TableType* expected = get<TableType>(tm->wantedType);
|
"type X | Y | Z[1] (Y) is not a subtype of { w: number } ({ w: number })\n\t"
|
||||||
REQUIRE(expected);
|
"type X | Y | Z[2] (Z) is not a subtype of { w: number } ({ w: number })");
|
||||||
CHECK_EQ(TableState::Sealed, expected->state);
|
}
|
||||||
CHECK_EQ(1, expected->props.size());
|
else
|
||||||
auto propW = expected->props.find("w");
|
{
|
||||||
REQUIRE_NE(expected->props.end(), propW);
|
CHECK_EQ(toString(result.errors[0]), R"(Type 'X | Y | Z' could not be converted into '{| w: number |}'
|
||||||
CHECK_EQ("number", toString(propW->second.type()));
|
caused by:
|
||||||
|
Not all union options are compatible.
|
||||||
|
Table type 'X' not compatible with type '{| w: number |}' because the former is missing field 'w')");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "error_detailed_union_all")
|
TEST_CASE_FIXTURE(Fixture, "error_detailed_union_all")
|
||||||
@ -545,14 +567,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_union_write_indirect")
|
|||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type A = { x: number, y: (number) -> string } | { z: number, y: (number) -> string }
|
type A = { x: number, y: (number) -> string } | { z: number, y: (number) -> string }
|
||||||
|
|
||||||
local a:A = nil
|
function f(a: A)
|
||||||
|
function a.y(x)
|
||||||
|
return tostring(x * 2)
|
||||||
|
end
|
||||||
|
|
||||||
function a.y(x)
|
function a.y(x: string): number
|
||||||
return tostring(x * 2)
|
return tonumber(x) or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function a.y(x: string): number
|
|
||||||
return tonumber(x) or 0
|
|
||||||
end
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
@ -568,12 +590,13 @@ could not be converted into
|
|||||||
TEST_CASE_FIXTURE(Fixture, "union_true_and_false")
|
TEST_CASE_FIXTURE(Fixture, "union_true_and_false")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : boolean
|
function f(x : boolean)
|
||||||
local y1 : (true | false) = x -- OK
|
local y1 : (true | false) = x -- OK
|
||||||
local y2 : (true | false | (string & number)) = x -- OK
|
local y2 : (true | false | (string & number)) = x -- OK
|
||||||
local y3 : (true | (string & number) | false) = x -- OK
|
local y3 : (true | (string & number) | false) = x -- OK
|
||||||
local y4 : (true | (boolean & true) | false) = x -- OK
|
local y4 : (true | (boolean & true) | false) = x -- OK
|
||||||
)");
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
}
|
}
|
||||||
@ -581,8 +604,9 @@ TEST_CASE_FIXTURE(Fixture, "union_true_and_false")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions")
|
TEST_CASE_FIXTURE(Fixture, "union_of_functions")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : (number) -> number?
|
function f(x : (number) -> number?)
|
||||||
local y : ((number?) -> number?) | ((number) -> number) = x -- OK
|
local y : ((number?) -> number?) | ((number) -> number) = x -- OK
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
@ -591,8 +615,9 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "union_of_generic_functions")
|
TEST_CASE_FIXTURE(Fixture, "union_of_generic_functions")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : <a>(a) -> a?
|
function f(x : <a>(a) -> a?)
|
||||||
local y : (<a>(a?) -> a?) | (<b>(b) -> b) = x -- Not OK
|
local y : (<a>(a?) -> a?) | (<b>(b) -> b) = x -- Not OK
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
// TODO: should this example typecheck?
|
// TODO: should this example typecheck?
|
||||||
@ -602,8 +627,9 @@ TEST_CASE_FIXTURE(Fixture, "union_of_generic_functions")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "union_of_generic_typepack_functions")
|
TEST_CASE_FIXTURE(Fixture, "union_of_generic_typepack_functions")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : <a...>(number, a...) -> (number?, a...)
|
function f(x : <a...>(number, a...) -> (number?, a...))
|
||||||
local y : (<a...>(number?, a...) -> (number?, a...)) | (<b...>(number, b...) -> (number, b...)) = x -- Not OK
|
local y : (<a...>(number?, a...) -> (number?, a...)) | (<b...>(number, b...) -> (number, b...)) = x -- Not OK
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
// TODO: should this example typecheck?
|
// TODO: should this example typecheck?
|
||||||
@ -613,12 +639,13 @@ TEST_CASE_FIXTURE(Fixture, "union_of_generic_typepack_functions")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generics")
|
TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generics")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a,b>()
|
function f<a,b>()
|
||||||
local x : (a) -> a?
|
function g(x : (a) -> a?)
|
||||||
local y : ((a?) -> nil) | ((a) -> a) = x -- OK
|
local y : ((a?) -> nil) | ((a) -> a) = x -- OK
|
||||||
local z : ((b?) -> nil) | ((b) -> b) = x -- Not OK
|
local z : ((b?) -> nil) | ((b) -> b) = x -- Not OK
|
||||||
end
|
end
|
||||||
)");
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
CHECK_EQ(toString(result.errors[0]),
|
CHECK_EQ(toString(result.errors[0]),
|
||||||
@ -628,12 +655,13 @@ TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generics")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generic_typepacks")
|
TEST_CASE_FIXTURE(Fixture, "union_of_functions_mentioning_generic_typepacks")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
function f<a...>()
|
function f<a...>()
|
||||||
local x : (number, a...) -> (number?, a...)
|
function g(x : (number, a...) -> (number?, a...))
|
||||||
local y : ((number | string, a...) -> (number, a...)) | ((number?, a...) -> (nil, a...)) = x -- OK
|
local y : ((number | string, a...) -> (number, a...)) | ((number?, a...) -> (nil, a...)) = x -- OK
|
||||||
local z : ((number) -> number) | ((number?, a...) -> (number?, a...)) = x -- Not OK
|
local z : ((number) -> number) | ((number?, a...) -> (number?, a...)) = x -- Not OK
|
||||||
end
|
end
|
||||||
)");
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
const std::string expected = R"(Type
|
const std::string expected = R"(Type
|
||||||
@ -646,9 +674,10 @@ could not be converted into
|
|||||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_arities")
|
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_arities")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : (number) -> number?
|
function f(x : (number) -> number?)
|
||||||
local y : ((number?) -> number) | ((number | string) -> nil) = x -- OK
|
local y : ((number?) -> number) | ((number | string) -> nil) = x -- OK
|
||||||
local z : ((number, string?) -> number) | ((number) -> nil) = x -- Not OK
|
local z : ((number, string?) -> number) | ((number) -> nil) = x -- Not OK
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -661,11 +690,11 @@ could not be converted into
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_arities")
|
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_arities")
|
||||||
{
|
{
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : () -> (number | string)
|
function f(x : () -> (number | string))
|
||||||
local y : (() -> number) | (() -> string) = x -- OK
|
local y : (() -> number) | (() -> string) = x -- OK
|
||||||
local z : (() -> number) | (() -> (string, string)) = x -- Not OK
|
local z : (() -> number) | (() -> (string, string)) = x -- Not OK
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -678,11 +707,11 @@ could not be converted into
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_variadics")
|
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_variadics")
|
||||||
{
|
{
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : (...nil) -> (...number?)
|
function f(x : (...nil) -> (...number?))
|
||||||
local y : ((...string?) -> (...number)) | ((...number?) -> nil) = x -- OK
|
local y : ((...string?) -> (...number)) | ((...number?) -> nil) = x -- OK
|
||||||
local z : ((...string?) -> (...number)) | ((...string?) -> nil) = x -- OK
|
local z : ((...string?) -> (...number)) | ((...string?) -> nil) = x -- OK
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -695,11 +724,11 @@ could not be converted into
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_variadics")
|
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_arg_variadics")
|
||||||
{
|
{
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : (number) -> ()
|
function f(x : (number) -> ())
|
||||||
local y : ((number?) -> ()) | ((...number) -> ()) = x -- OK
|
local y : ((number?) -> ()) | ((...number) -> ()) = x -- OK
|
||||||
local z : ((number?) -> ()) | ((...number?) -> ()) = x -- Not OK
|
local z : ((number?) -> ()) | ((...number?) -> ()) = x -- Not OK
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -712,11 +741,11 @@ could not be converted into
|
|||||||
|
|
||||||
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_variadics")
|
TEST_CASE_FIXTURE(Fixture, "union_of_functions_with_mismatching_result_variadics")
|
||||||
{
|
{
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local x : () -> (number?, ...number)
|
function f(x : () -> (number?, ...number))
|
||||||
local y : (() -> (...number)) | (() -> nil) = x -- OK
|
local y : (() -> (...number)) | (() -> nil) = x -- OK
|
||||||
local z : (() -> (...number)) | (() -> number) = x -- OK
|
local z : (() -> (...number)) | (() -> number) = x -- OK
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
@ -782,9 +811,9 @@ TEST_CASE_FIXTURE(Fixture, "union_table_any_property")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "union_function_any_args")
|
TEST_CASE_FIXTURE(Fixture, "union_function_any_args")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local sup : ((...any) -> (...any))?
|
function f(sup : ((...any) -> (...any))?, sub : ((number) -> (...any)))
|
||||||
local sub : ((number) -> (...any))
|
sup = sub
|
||||||
sup = sub
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
@ -793,9 +822,9 @@ TEST_CASE_FIXTURE(Fixture, "union_function_any_args")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "optional_any")
|
TEST_CASE_FIXTURE(Fixture, "optional_any")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local sup : any?
|
function f(sup : any?, sub : number)
|
||||||
local sub : number
|
sup = sub
|
||||||
sup = sub
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
@ -844,9 +873,9 @@ TEST_CASE_FIXTURE(Fixture, "suppress_errors_for_prop_lookup_of_a_union_that_incl
|
|||||||
registerHiddenTypes(&frontend);
|
registerHiddenTypes(&frontend);
|
||||||
|
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local a = 5 :: err | Not<nil>
|
function f(a: err | Not<nil>)
|
||||||
|
local b = a.foo
|
||||||
local b = a.foo
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
@ -215,8 +215,9 @@ TEST_CASE_FIXTURE(Fixture, "assign_to_global_which_is_never")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "assign_to_prop_which_is_never")
|
TEST_CASE_FIXTURE(Fixture, "assign_to_prop_which_is_never")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: never
|
local function f(t: never)
|
||||||
t.x = 5
|
t.x = 5
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
@ -225,8 +226,9 @@ TEST_CASE_FIXTURE(Fixture, "assign_to_prop_which_is_never")
|
|||||||
TEST_CASE_FIXTURE(Fixture, "assign_to_subscript_which_is_never")
|
TEST_CASE_FIXTURE(Fixture, "assign_to_subscript_which_is_never")
|
||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
local t: never
|
local function f(t: never)
|
||||||
t[5] = 7
|
t[5] = 7
|
||||||
|
end
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
@ -257,8 +259,12 @@ TEST_CASE_FIXTURE(Fixture, "index_on_union_of_tables_for_properties_that_is_neve
|
|||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type Disjoint = {foo: never, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"}
|
type Disjoint = {foo: never, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"}
|
||||||
local disjoint: Disjoint = {foo = 5 :: never, bar = true, tag = "ok"}
|
|
||||||
local foo = disjoint.foo
|
function f(disjoint: Disjoint)
|
||||||
|
return disjoint.foo
|
||||||
|
end
|
||||||
|
|
||||||
|
local foo = f({foo = 5 :: never, bar = true, tag = "ok"})
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
@ -270,8 +276,12 @@ TEST_CASE_FIXTURE(Fixture, "index_on_union_of_tables_for_properties_that_is_sort
|
|||||||
{
|
{
|
||||||
CheckResult result = check(R"(
|
CheckResult result = check(R"(
|
||||||
type Disjoint = {foo: string, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"}
|
type Disjoint = {foo: string, bar: unknown, tag: "ok"} | {foo: never, baz: unknown, tag: "err"}
|
||||||
local disjoint: Disjoint = {foo = 5 :: never, bar = true, tag = "ok"}
|
|
||||||
local foo = disjoint.foo
|
function f(disjoint: Disjoint)
|
||||||
|
return disjoint.foo
|
||||||
|
end
|
||||||
|
|
||||||
|
local foo = f({foo = 5 :: never, bar = true, tag = "ok"})
|
||||||
)");
|
)");
|
||||||
|
|
||||||
LUAU_REQUIRE_NO_ERRORS(result);
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
-- should be required using the paths array in the parent directory's .luaurc
|
-- should be required using the paths array in the parent directory's .luaurc
|
||||||
return require("@global_library")
|
return require("global_library")
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
-- should be required using the paths array in .luaurc
|
-- should be required using the paths array in .luaurc
|
||||||
return require("@library")
|
return require("library")
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
local result = require("./dependency")
|
local result = require("dependency")
|
||||||
result[#result+1] = "required into module"
|
result[#result+1] = "required into module"
|
||||||
return result
|
return result
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
AnnotationTests.typeof_expr
|
AnnotationTests.typeof_expr
|
||||||
AstQuery.last_argument_function_call_type
|
AstQuery.last_argument_function_call_type
|
||||||
AstQuery::getDocumentationSymbolAtPosition.class_method
|
|
||||||
AstQuery::getDocumentationSymbolAtPosition.overloaded_class_method
|
|
||||||
AstQuery::getDocumentationSymbolAtPosition.table_overloaded_function_prop
|
AstQuery::getDocumentationSymbolAtPosition.table_overloaded_function_prop
|
||||||
AutocompleteTest.anonymous_autofilled_generic_on_argument_type_pack_vararg
|
AutocompleteTest.anonymous_autofilled_generic_on_argument_type_pack_vararg
|
||||||
AutocompleteTest.anonymous_autofilled_generic_type_pack_vararg
|
AutocompleteTest.anonymous_autofilled_generic_type_pack_vararg
|
||||||
AutocompleteTest.autocomplete_interpolated_string_as_singleton
|
AutocompleteTest.autocomplete_interpolated_string_as_singleton
|
||||||
AutocompleteTest.autocomplete_response_perf1
|
|
||||||
AutocompleteTest.autocomplete_string_singleton_equality
|
AutocompleteTest.autocomplete_string_singleton_equality
|
||||||
AutocompleteTest.autocomplete_string_singleton_escape
|
AutocompleteTest.autocomplete_string_singleton_escape
|
||||||
AutocompleteTest.autocomplete_string_singletons
|
AutocompleteTest.autocomplete_string_singletons
|
||||||
AutocompleteTest.do_wrong_compatible_nonself_calls
|
AutocompleteTest.do_wrong_compatible_nonself_calls
|
||||||
AutocompleteTest.no_incompatible_self_calls_on_class
|
|
||||||
AutocompleteTest.string_singleton_in_if_statement
|
AutocompleteTest.string_singleton_in_if_statement
|
||||||
AutocompleteTest.suggest_external_module_type
|
AutocompleteTest.suggest_external_module_type
|
||||||
AutocompleteTest.type_correct_expected_argument_type_pack_suggestion
|
AutocompleteTest.type_correct_expected_argument_type_pack_suggestion
|
||||||
@ -33,7 +29,6 @@ BuiltinTests.assert_returns_false_and_string_iff_it_knows_the_first_argument_can
|
|||||||
BuiltinTests.bad_select_should_not_crash
|
BuiltinTests.bad_select_should_not_crash
|
||||||
BuiltinTests.coroutine_resume_anything_goes
|
BuiltinTests.coroutine_resume_anything_goes
|
||||||
BuiltinTests.debug_info_is_crazy
|
BuiltinTests.debug_info_is_crazy
|
||||||
BuiltinTests.dont_add_definitions_to_persistent_types
|
|
||||||
BuiltinTests.global_singleton_types_are_sealed
|
BuiltinTests.global_singleton_types_are_sealed
|
||||||
BuiltinTests.gmatch_capture_types
|
BuiltinTests.gmatch_capture_types
|
||||||
BuiltinTests.gmatch_capture_types2
|
BuiltinTests.gmatch_capture_types2
|
||||||
@ -62,7 +57,6 @@ BuiltinTests.string_format_as_method
|
|||||||
BuiltinTests.string_format_correctly_ordered_types
|
BuiltinTests.string_format_correctly_ordered_types
|
||||||
BuiltinTests.string_format_report_all_type_errors_at_correct_positions
|
BuiltinTests.string_format_report_all_type_errors_at_correct_positions
|
||||||
BuiltinTests.string_format_use_correct_argument2
|
BuiltinTests.string_format_use_correct_argument2
|
||||||
BuiltinTests.string_match
|
|
||||||
BuiltinTests.table_concat_returns_string
|
BuiltinTests.table_concat_returns_string
|
||||||
BuiltinTests.table_dot_remove_optionally_returns_generic
|
BuiltinTests.table_dot_remove_optionally_returns_generic
|
||||||
BuiltinTests.table_freeze_is_generic
|
BuiltinTests.table_freeze_is_generic
|
||||||
@ -100,16 +94,15 @@ ControlFlowAnalysis.if_not_x_then_assert_false
|
|||||||
ControlFlowAnalysis.if_not_x_then_error
|
ControlFlowAnalysis.if_not_x_then_error
|
||||||
ControlFlowAnalysis.prototyping_and_visiting_alias_has_the_same_scope_breaking
|
ControlFlowAnalysis.prototyping_and_visiting_alias_has_the_same_scope_breaking
|
||||||
ControlFlowAnalysis.prototyping_and_visiting_alias_has_the_same_scope_continuing
|
ControlFlowAnalysis.prototyping_and_visiting_alias_has_the_same_scope_continuing
|
||||||
|
ControlFlowAnalysis.tagged_unions
|
||||||
ControlFlowAnalysis.tagged_unions_breaking
|
ControlFlowAnalysis.tagged_unions_breaking
|
||||||
ControlFlowAnalysis.tagged_unions_continuing
|
ControlFlowAnalysis.tagged_unions_continuing
|
||||||
ControlFlowAnalysis.type_alias_does_not_leak_out_breaking
|
ControlFlowAnalysis.type_alias_does_not_leak_out_breaking
|
||||||
ControlFlowAnalysis.type_alias_does_not_leak_out_continuing
|
ControlFlowAnalysis.type_alias_does_not_leak_out_continuing
|
||||||
DefinitionTests.class_definition_function_prop
|
|
||||||
DefinitionTests.class_definition_indexer
|
DefinitionTests.class_definition_indexer
|
||||||
DefinitionTests.class_definition_overload_metamethods
|
DefinitionTests.class_definition_overload_metamethods
|
||||||
DefinitionTests.class_definition_string_props
|
DefinitionTests.class_definition_string_props
|
||||||
DefinitionTests.declaring_generic_functions
|
DefinitionTests.declaring_generic_functions
|
||||||
DefinitionTests.definition_file_class_function_args
|
|
||||||
DefinitionTests.definition_file_classes
|
DefinitionTests.definition_file_classes
|
||||||
Differ.equal_generictp_cyclic
|
Differ.equal_generictp_cyclic
|
||||||
Differ.equal_table_A_B_C
|
Differ.equal_table_A_B_C
|
||||||
@ -142,7 +135,6 @@ GenericsTests.apply_type_function_nested_generics1
|
|||||||
GenericsTests.apply_type_function_nested_generics2
|
GenericsTests.apply_type_function_nested_generics2
|
||||||
GenericsTests.better_mismatch_error_messages
|
GenericsTests.better_mismatch_error_messages
|
||||||
GenericsTests.bound_tables_do_not_clone_original_fields
|
GenericsTests.bound_tables_do_not_clone_original_fields
|
||||||
GenericsTests.calling_self_generic_methods
|
|
||||||
GenericsTests.check_generic_function
|
GenericsTests.check_generic_function
|
||||||
GenericsTests.check_generic_local_function
|
GenericsTests.check_generic_local_function
|
||||||
GenericsTests.check_generic_typepack_function
|
GenericsTests.check_generic_typepack_function
|
||||||
@ -230,6 +222,7 @@ Linter.DeprecatedApiFenv
|
|||||||
Linter.FormatStringTyped
|
Linter.FormatStringTyped
|
||||||
Linter.TableOperationsIndexer
|
Linter.TableOperationsIndexer
|
||||||
ModuleTests.clone_self_property
|
ModuleTests.clone_self_property
|
||||||
|
Negations.cofinite_strings_can_be_compared_for_equality
|
||||||
Negations.negated_string_is_a_subtype_of_string
|
Negations.negated_string_is_a_subtype_of_string
|
||||||
NonstrictModeTests.inconsistent_module_return_types_are_ok
|
NonstrictModeTests.inconsistent_module_return_types_are_ok
|
||||||
NonstrictModeTests.infer_nullary_function
|
NonstrictModeTests.infer_nullary_function
|
||||||
@ -244,6 +237,7 @@ Normalize.negations_of_tables
|
|||||||
Normalize.specific_functions_cannot_be_negated
|
Normalize.specific_functions_cannot_be_negated
|
||||||
ProvisionalTests.assign_table_with_refined_property_with_a_similar_type_is_illegal
|
ProvisionalTests.assign_table_with_refined_property_with_a_similar_type_is_illegal
|
||||||
ProvisionalTests.choose_the_right_overload_for_pcall
|
ProvisionalTests.choose_the_right_overload_for_pcall
|
||||||
|
ProvisionalTests.discriminate_from_x_not_equal_to_nil
|
||||||
ProvisionalTests.do_not_ice_when_trying_to_pick_first_of_generic_type_pack
|
ProvisionalTests.do_not_ice_when_trying_to_pick_first_of_generic_type_pack
|
||||||
ProvisionalTests.error_on_eq_metamethod_returning_a_type_other_than_boolean
|
ProvisionalTests.error_on_eq_metamethod_returning_a_type_other_than_boolean
|
||||||
ProvisionalTests.expected_type_should_be_a_helpful_deduction_guide_for_function_calls
|
ProvisionalTests.expected_type_should_be_a_helpful_deduction_guide_for_function_calls
|
||||||
@ -272,19 +266,27 @@ RefinementTest.assert_non_binary_expressions_actually_resolve_constraints
|
|||||||
RefinementTest.correctly_lookup_a_shadowed_local_that_which_was_previously_refined
|
RefinementTest.correctly_lookup_a_shadowed_local_that_which_was_previously_refined
|
||||||
RefinementTest.correctly_lookup_property_whose_base_was_previously_refined
|
RefinementTest.correctly_lookup_property_whose_base_was_previously_refined
|
||||||
RefinementTest.dataflow_analysis_can_tell_refinements_when_its_appropriate_to_refine_into_nil_or_never
|
RefinementTest.dataflow_analysis_can_tell_refinements_when_its_appropriate_to_refine_into_nil_or_never
|
||||||
|
RefinementTest.discriminate_from_isa_of_x
|
||||||
RefinementTest.discriminate_from_truthiness_of_x
|
RefinementTest.discriminate_from_truthiness_of_x
|
||||||
|
RefinementTest.discriminate_tag
|
||||||
|
RefinementTest.discriminate_tag_with_implicit_else
|
||||||
RefinementTest.either_number_or_string
|
RefinementTest.either_number_or_string
|
||||||
|
RefinementTest.else_with_no_explicit_expression_should_also_refine_the_tagged_union
|
||||||
RefinementTest.fail_to_refine_a_property_of_subscript_expression
|
RefinementTest.fail_to_refine_a_property_of_subscript_expression
|
||||||
RefinementTest.falsiness_of_TruthyPredicate_narrows_into_nil
|
RefinementTest.falsiness_of_TruthyPredicate_narrows_into_nil
|
||||||
RefinementTest.function_call_with_colon_after_refining_not_to_be_nil
|
RefinementTest.function_call_with_colon_after_refining_not_to_be_nil
|
||||||
RefinementTest.impossible_type_narrow_is_not_an_error
|
RefinementTest.impossible_type_narrow_is_not_an_error
|
||||||
RefinementTest.index_on_a_refined_property
|
RefinementTest.index_on_a_refined_property
|
||||||
RefinementTest.isa_type_refinement_must_be_known_ahead_of_time
|
RefinementTest.isa_type_refinement_must_be_known_ahead_of_time
|
||||||
|
RefinementTest.luau_polyfill_isindexkey_refine_conjunction
|
||||||
|
RefinementTest.luau_polyfill_isindexkey_refine_conjunction_variant
|
||||||
RefinementTest.merge_should_be_fully_agnostic_of_hashmap_ordering
|
RefinementTest.merge_should_be_fully_agnostic_of_hashmap_ordering
|
||||||
RefinementTest.narrow_property_of_a_bounded_variable
|
RefinementTest.narrow_property_of_a_bounded_variable
|
||||||
RefinementTest.nonoptional_type_can_narrow_to_nil_if_sense_is_true
|
RefinementTest.nonoptional_type_can_narrow_to_nil_if_sense_is_true
|
||||||
RefinementTest.not_t_or_some_prop_of_t
|
RefinementTest.not_t_or_some_prop_of_t
|
||||||
|
RefinementTest.refine_a_param_that_got_resolved_during_constraint_solving_stage
|
||||||
RefinementTest.refine_a_property_of_some_global
|
RefinementTest.refine_a_property_of_some_global
|
||||||
|
RefinementTest.refine_param_of_type_folder_or_part_without_using_typeof
|
||||||
RefinementTest.refine_unknown_to_table_then_clone_it
|
RefinementTest.refine_unknown_to_table_then_clone_it
|
||||||
RefinementTest.refinements_should_preserve_error_suppression
|
RefinementTest.refinements_should_preserve_error_suppression
|
||||||
RefinementTest.string_not_equal_to_string_or_nil
|
RefinementTest.string_not_equal_to_string_or_nil
|
||||||
@ -297,6 +299,7 @@ RefinementTest.typeguard_cast_free_table_to_vector
|
|||||||
RefinementTest.typeguard_in_assert_position
|
RefinementTest.typeguard_in_assert_position
|
||||||
RefinementTest.typeguard_in_if_condition_position
|
RefinementTest.typeguard_in_if_condition_position
|
||||||
RefinementTest.x_as_any_if_x_is_instance_elseif_x_is_table
|
RefinementTest.x_as_any_if_x_is_instance_elseif_x_is_table
|
||||||
|
RefinementTest.x_is_not_instance_or_else_not_part
|
||||||
TableTests.a_free_shape_can_turn_into_a_scalar_directly
|
TableTests.a_free_shape_can_turn_into_a_scalar_directly
|
||||||
TableTests.a_free_shape_can_turn_into_a_scalar_if_it_is_compatible
|
TableTests.a_free_shape_can_turn_into_a_scalar_if_it_is_compatible
|
||||||
TableTests.a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible
|
TableTests.a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible
|
||||||
@ -459,7 +462,6 @@ TypeInfer.cli_50041_committing_txnlog_in_apollo_client_error
|
|||||||
TypeInfer.dont_ice_when_failing_the_occurs_check
|
TypeInfer.dont_ice_when_failing_the_occurs_check
|
||||||
TypeInfer.dont_report_type_errors_within_an_AstExprError
|
TypeInfer.dont_report_type_errors_within_an_AstExprError
|
||||||
TypeInfer.dont_report_type_errors_within_an_AstStatError
|
TypeInfer.dont_report_type_errors_within_an_AstStatError
|
||||||
TypeInfer.follow_on_new_types_in_substitution
|
|
||||||
TypeInfer.globals
|
TypeInfer.globals
|
||||||
TypeInfer.globals2
|
TypeInfer.globals2
|
||||||
TypeInfer.globals_are_banned_in_strict_mode
|
TypeInfer.globals_are_banned_in_strict_mode
|
||||||
@ -555,6 +557,7 @@ TypeInferFunctions.other_things_are_not_related_to_function
|
|||||||
TypeInferFunctions.param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible
|
TypeInferFunctions.param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible
|
||||||
TypeInferFunctions.param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible_2
|
TypeInferFunctions.param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible_2
|
||||||
TypeInferFunctions.record_matching_overload
|
TypeInferFunctions.record_matching_overload
|
||||||
|
TypeInferFunctions.regex_benchmark_string_format_minimization
|
||||||
TypeInferFunctions.report_exiting_without_return_nonstrict
|
TypeInferFunctions.report_exiting_without_return_nonstrict
|
||||||
TypeInferFunctions.report_exiting_without_return_strict
|
TypeInferFunctions.report_exiting_without_return_strict
|
||||||
TypeInferFunctions.return_type_by_overload
|
TypeInferFunctions.return_type_by_overload
|
||||||
@ -600,6 +603,7 @@ TypeInferLoops.unreachable_code_after_infinite_loop
|
|||||||
TypeInferLoops.varlist_declared_by_for_in_loop_should_be_free
|
TypeInferLoops.varlist_declared_by_for_in_loop_should_be_free
|
||||||
TypeInferLoops.while_loop
|
TypeInferLoops.while_loop
|
||||||
TypeInferModules.bound_free_table_export_is_ok
|
TypeInferModules.bound_free_table_export_is_ok
|
||||||
|
TypeInferModules.custom_require_global
|
||||||
TypeInferModules.do_not_modify_imported_types
|
TypeInferModules.do_not_modify_imported_types
|
||||||
TypeInferModules.do_not_modify_imported_types_5
|
TypeInferModules.do_not_modify_imported_types_5
|
||||||
TypeInferModules.require
|
TypeInferModules.require
|
||||||
@ -645,7 +649,6 @@ TypeInferPrimitives.CheckMethodsOfNumber
|
|||||||
TypeInferPrimitives.string_function_indirect
|
TypeInferPrimitives.string_function_indirect
|
||||||
TypeInferPrimitives.string_index
|
TypeInferPrimitives.string_index
|
||||||
TypeInferUnknownNever.assign_to_local_which_is_never
|
TypeInferUnknownNever.assign_to_local_which_is_never
|
||||||
TypeInferUnknownNever.assign_to_prop_which_is_never
|
|
||||||
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_never
|
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_never
|
||||||
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_sorta_never
|
TypeInferUnknownNever.index_on_union_of_tables_for_properties_that_is_sorta_never
|
||||||
TypeInferUnknownNever.length_of_never
|
TypeInferUnknownNever.length_of_never
|
||||||
@ -663,42 +666,25 @@ TypeSingletons.error_detailed_tagged_union_mismatch_string
|
|||||||
TypeSingletons.function_args_infer_singletons
|
TypeSingletons.function_args_infer_singletons
|
||||||
TypeSingletons.function_call_with_singletons
|
TypeSingletons.function_call_with_singletons
|
||||||
TypeSingletons.function_call_with_singletons_mismatch
|
TypeSingletons.function_call_with_singletons_mismatch
|
||||||
TypeSingletons.overloaded_function_call_with_singletons_mismatch
|
|
||||||
TypeSingletons.return_type_of_f_is_not_widened
|
TypeSingletons.return_type_of_f_is_not_widened
|
||||||
TypeSingletons.table_properties_type_error_escapes
|
TypeSingletons.table_properties_type_error_escapes
|
||||||
TypeSingletons.widen_the_supertype_if_it_is_free_and_subtype_has_singleton
|
TypeSingletons.widen_the_supertype_if_it_is_free_and_subtype_has_singleton
|
||||||
TypeSingletons.widening_happens_almost_everywhere
|
|
||||||
TypeStatesTest.invalidate_type_refinements_upon_assignments
|
TypeStatesTest.invalidate_type_refinements_upon_assignments
|
||||||
TypeStatesTest.local_t_is_assigned_a_fresh_table_with_x_assigned_a_union_and_then_assert_restricts_actual_outflow_of_types
|
|
||||||
UnionTypes.disallow_less_specific_assign
|
|
||||||
UnionTypes.disallow_less_specific_assign2
|
|
||||||
UnionTypes.error_detailed_optional
|
UnionTypes.error_detailed_optional
|
||||||
UnionTypes.error_detailed_union_all
|
UnionTypes.error_detailed_union_all
|
||||||
UnionTypes.error_detailed_union_part
|
|
||||||
UnionTypes.error_takes_optional_arguments
|
UnionTypes.error_takes_optional_arguments
|
||||||
UnionTypes.generic_function_with_optional_arg
|
UnionTypes.generic_function_with_optional_arg
|
||||||
UnionTypes.index_on_a_union_type_with_missing_property
|
UnionTypes.index_on_a_union_type_with_missing_property
|
||||||
UnionTypes.index_on_a_union_type_with_mixed_types
|
|
||||||
UnionTypes.index_on_a_union_type_with_one_optional_property
|
|
||||||
UnionTypes.index_on_a_union_type_with_one_property_of_type_any
|
|
||||||
UnionTypes.index_on_a_union_type_with_property_guaranteed_to_exist
|
|
||||||
UnionTypes.index_on_a_union_type_works_at_arbitrary_depth
|
|
||||||
UnionTypes.less_greedy_unification_with_union_types
|
UnionTypes.less_greedy_unification_with_union_types
|
||||||
UnionTypes.optional_arguments_table
|
UnionTypes.optional_arguments_table
|
||||||
UnionTypes.optional_assignment_errors
|
|
||||||
UnionTypes.optional_call_error
|
|
||||||
UnionTypes.optional_field_access_error
|
|
||||||
UnionTypes.optional_index_error
|
|
||||||
UnionTypes.optional_length_error
|
UnionTypes.optional_length_error
|
||||||
UnionTypes.optional_missing_key_error_details
|
|
||||||
UnionTypes.optional_union_follow
|
|
||||||
UnionTypes.optional_union_functions
|
UnionTypes.optional_union_functions
|
||||||
UnionTypes.optional_union_members
|
UnionTypes.optional_union_members
|
||||||
UnionTypes.optional_union_methods
|
UnionTypes.optional_union_methods
|
||||||
UnionTypes.return_types_can_be_disjoint
|
UnionTypes.return_types_can_be_disjoint
|
||||||
UnionTypes.table_union_write_indirect
|
UnionTypes.table_union_write_indirect
|
||||||
UnionTypes.unify_unsealed_table_union_check
|
UnionTypes.unify_unsealed_table_union_check
|
||||||
UnionTypes.union_of_functions
|
UnionTypes.union_function_any_args
|
||||||
UnionTypes.union_of_functions_mentioning_generic_typepacks
|
UnionTypes.union_of_functions_mentioning_generic_typepacks
|
||||||
UnionTypes.union_of_functions_mentioning_generics
|
UnionTypes.union_of_functions_mentioning_generics
|
||||||
UnionTypes.union_of_functions_with_mismatching_arg_arities
|
UnionTypes.union_of_functions_with_mismatching_arg_arities
|
||||||
@ -706,5 +692,4 @@ UnionTypes.union_of_functions_with_mismatching_arg_variadics
|
|||||||
UnionTypes.union_of_functions_with_mismatching_result_arities
|
UnionTypes.union_of_functions_with_mismatching_result_arities
|
||||||
UnionTypes.union_of_functions_with_mismatching_result_variadics
|
UnionTypes.union_of_functions_with_mismatching_result_variadics
|
||||||
UnionTypes.union_of_functions_with_variadics
|
UnionTypes.union_of_functions_with_variadics
|
||||||
UnionTypes.union_true_and_false
|
|
||||||
VisitType.throw_when_limit_is_exceeded
|
VisitType.throw_when_limit_is_exceeded
|
||||||
|
Loading…
Reference in New Issue
Block a user