luau/tests/ToDot.test.cpp

396 lines
8.8 KiB
C++
Raw Normal View History

2021-12-03 07:20:08 +08:00
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Scope.h"
#include "Luau/ToDot.h"
#include "Fixture.h"
#include "doctest.h"
using namespace Luau;
struct ToDotClassFixture : Fixture
{
ToDotClassFixture()
{
TypeArena& arena = typeChecker.globalTypes;
unfreeze(arena);
2023-01-04 01:33:19 +08:00
TypeId baseClassMetaType = arena.addType(TableType{});
2021-12-03 07:20:08 +08:00
2023-01-04 01:33:19 +08:00
TypeId baseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, std::nullopt, baseClassMetaType, {}, {}, "Test"});
getMutable<ClassType>(baseClassInstanceType)->props = {
2021-12-03 07:20:08 +08:00
{"BaseField", {typeChecker.numberType}},
};
typeChecker.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
2023-01-04 01:33:19 +08:00
TypeId childClassInstanceType = arena.addType(ClassType{"ChildClass", {}, baseClassInstanceType, std::nullopt, {}, {}, "Test"});
getMutable<ClassType>(childClassInstanceType)->props = {
2021-12-03 07:20:08 +08:00
{"ChildField", {typeChecker.stringType}},
};
typeChecker.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType};
2022-05-27 04:33:48 +08:00
for (const auto& [name, ty] : typeChecker.globalScope->exportedTypeBindings)
persist(ty.type);
2021-12-03 07:20:08 +08:00
freeze(arena);
}
};
TEST_SUITE_BEGIN("ToDot");
TEST_CASE_FIXTURE(Fixture, "primitive")
{
CheckResult result = check(R"(
local a: nil
local b: number
local c: any
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_NE("nil", toDot(requireType("a")));
CHECK_EQ(R"(digraph graphname {
n1 [label="number"];
})",
toDot(requireType("b")));
CHECK_EQ(R"(digraph graphname {
n1 [label="any"];
})",
toDot(requireType("c")));
ToDotOptions opts;
opts.showPointers = false;
opts.duplicatePrimitives = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="PrimitiveType number"];
2021-12-03 07:20:08 +08:00
})",
toDot(requireType("b"), opts));
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="AnyType 1"];
2021-12-03 07:20:08 +08:00
})",
toDot(requireType("c"), opts));
}
TEST_CASE_FIXTURE(Fixture, "bound")
{
CheckResult result = check(R"(
2022-10-22 01:33:43 +08:00
function a(): number return 444 end
local b = a()
2021-12-03 07:20:08 +08:00
)");
LUAU_REQUIRE_NO_ERRORS(result);
std::optional<TypeId> ty = getType("b");
REQUIRE(bool(ty));
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="BoundType 1"];
2021-12-03 07:20:08 +08:00
n1 -> n2;
n2 [label="number"];
})",
toDot(*ty, opts));
}
TEST_CASE_FIXTURE(Fixture, "function")
{
CheckResult result = check(R"(
local function f(a, ...: string) return a end
)");
LUAU_REQUIRE_NO_ERRORS(result);
2022-04-15 05:57:15 +08:00
CHECK_EQ("<a>(a, ...string) -> a", toString(requireType("f")));
2021-12-03 07:20:08 +08:00
ToDotOptions opts;
opts.showPointers = false;
2022-04-15 05:57:15 +08:00
2022-10-14 06:59:53 +08:00
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="FunctionType 1"];
2021-12-03 07:20:08 +08:00
n1 -> n2 [label="arg"];
n2 [label="TypePack 2"];
n2 -> n3;
2023-01-04 01:33:19 +08:00
n3 [label="GenericType 3"];
2021-12-03 07:20:08 +08:00
n2 -> n4 [label="tail"];
n4 [label="VariadicTypePack 4"];
n4 -> n5;
n5 [label="string"];
n1 -> n6 [label="ret"];
n6 [label="BoundTypePack 6"];
n6 -> n7;
n7 [label="TypePack 7"];
n7 -> n3;
})",
2022-10-14 06:59:53 +08:00
toDot(requireType("f"), opts));
2021-12-03 07:20:08 +08:00
}
TEST_CASE_FIXTURE(Fixture, "union")
{
CheckResult result = check(R"(
local a: string | number
)");
LUAU_REQUIRE_NO_ERRORS(result);
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="UnionType 1"];
2021-12-03 07:20:08 +08:00
n1 -> n2;
n2 [label="string"];
n1 -> n3;
n3 [label="number"];
})",
toDot(requireType("a"), opts));
}
TEST_CASE_FIXTURE(Fixture, "intersection")
{
CheckResult result = check(R"(
local a: string & number -- uninhabited
)");
LUAU_REQUIRE_NO_ERRORS(result);
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="IntersectionType 1"];
2021-12-03 07:20:08 +08:00
n1 -> n2;
n2 [label="string"];
n1 -> n3;
n3 [label="number"];
})",
toDot(requireType("a"), opts));
}
TEST_CASE_FIXTURE(Fixture, "table")
{
CheckResult result = check(R"(
type A<T, U...> = { x: T, y: (U...) -> (), [string]: any }
local a: A<number, ...string>
)");
LUAU_REQUIRE_NO_ERRORS(result);
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="TableType A"];
2021-12-03 07:20:08 +08:00
n1 -> n2 [label="x"];
n2 [label="number"];
n1 -> n3 [label="y"];
2023-01-04 01:33:19 +08:00
n3 [label="FunctionType 3"];
2021-12-03 07:20:08 +08:00
n3 -> n4 [label="arg"];
n4 [label="VariadicTypePack 4"];
n4 -> n5;
n5 [label="string"];
n3 -> n6 [label="ret"];
n6 [label="TypePack 6"];
n1 -> n7 [label="[index]"];
n7 [label="string"];
n1 -> n8 [label="[value]"];
n8 [label="any"];
n1 -> n9 [label="typeParam"];
n9 [label="number"];
n1 -> n4 [label="typePackParam"];
})",
toDot(requireType("a"), opts));
// Extra coverage with pointers (unstable values)
(void)toDot(requireType("a"));
}
2022-05-14 03:16:50 +08:00
TEST_CASE_FIXTURE(BuiltinsFixture, "metatable")
2021-12-03 07:20:08 +08:00
{
CheckResult result = check(R"(
local a: typeof(setmetatable({}, {}))
)");
LUAU_REQUIRE_NO_ERRORS(result);
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="MetatableType 1"];
2021-12-03 07:20:08 +08:00
n1 -> n2 [label="table"];
2023-01-04 01:33:19 +08:00
n2 [label="TableType 2"];
2021-12-03 07:20:08 +08:00
n1 -> n3 [label="metatable"];
2023-01-04 01:33:19 +08:00
n3 [label="TableType 3"];
2021-12-03 07:20:08 +08:00
})",
toDot(requireType("a"), opts));
}
TEST_CASE_FIXTURE(Fixture, "free")
{
2023-01-04 01:33:19 +08:00
Type type{TypeVariant{FreeType{TypeLevel{0, 0}}}};
2021-12-03 07:20:08 +08:00
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="FreeType 1"];
2021-12-03 07:20:08 +08:00
})",
toDot(&type, opts));
}
TEST_CASE_FIXTURE(Fixture, "error")
{
2023-01-04 01:33:19 +08:00
Type type{TypeVariant{ErrorType{}}};
2021-12-03 07:20:08 +08:00
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="ErrorType 1"];
2021-12-03 07:20:08 +08:00
})",
toDot(&type, opts));
}
TEST_CASE_FIXTURE(Fixture, "generic")
{
2023-01-04 01:33:19 +08:00
Type type{TypeVariant{GenericType{"T"}}};
2021-12-03 07:20:08 +08:00
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="GenericType T"];
2021-12-03 07:20:08 +08:00
})",
toDot(&type, opts));
}
TEST_CASE_FIXTURE(ToDotClassFixture, "class")
{
CheckResult result = check(R"(
local a: ChildClass
)");
LUAU_REQUIRE_NO_ERRORS(result);
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="ClassType ChildClass"];
2021-12-03 07:20:08 +08:00
n1 -> n2 [label="ChildField"];
n2 [label="string"];
n1 -> n3 [label="[parent]"];
2023-01-04 01:33:19 +08:00
n3 [label="ClassType BaseClass"];
2021-12-03 07:20:08 +08:00
n3 -> n4 [label="BaseField"];
n4 [label="number"];
n3 -> n5 [label="[metatable]"];
2023-01-04 01:33:19 +08:00
n5 [label="TableType 5"];
2021-12-03 07:20:08 +08:00
})",
toDot(requireType("a"), opts));
}
TEST_CASE_FIXTURE(Fixture, "free_pack")
{
TypePackVar pack{TypePackVariant{FreeTypePack{TypeLevel{0, 0}}}};
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
n1 [label="FreeTypePack 1"];
})",
toDot(&pack, opts));
}
TEST_CASE_FIXTURE(Fixture, "error_pack")
{
TypePackVar pack{TypePackVariant{Unifiable::Error{}}};
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
n1 [label="ErrorTypePack 1"];
})",
toDot(&pack, opts));
// Extra coverage with pointers (unstable values)
(void)toDot(&pack);
}
TEST_CASE_FIXTURE(Fixture, "generic_pack")
{
TypePackVar pack1{TypePackVariant{GenericTypePack{}}};
TypePackVar pack2{TypePackVariant{GenericTypePack{"T"}}};
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
n1 [label="GenericTypePack 1"];
})",
toDot(&pack1, opts));
CHECK_EQ(R"(digraph graphname {
n1 [label="GenericTypePack T"];
})",
toDot(&pack2, opts));
}
TEST_CASE_FIXTURE(Fixture, "bound_pack")
{
TypePackVar pack{TypePackVariant{TypePack{{typeChecker.numberType}, {}}}};
TypePackVar bound{TypePackVariant{BoundTypePack{&pack}}};
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
n1 [label="BoundTypePack 1"];
n1 -> n2;
n2 [label="TypePack 2"];
n2 -> n3;
n3 [label="number"];
})",
toDot(&bound, opts));
}
TEST_CASE_FIXTURE(Fixture, "bound_table")
{
CheckResult result = check(R"(
local a = {x=2}
local b
b.x = 2
b = a
)");
LUAU_REQUIRE_NO_ERRORS(result);
std::optional<TypeId> ty = getType("b");
REQUIRE(bool(ty));
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="TableType 1"];
2021-12-03 07:20:08 +08:00
n1 -> n2 [label="boundTo"];
2023-01-04 01:33:19 +08:00
n2 [label="TableType a"];
2021-12-03 07:20:08 +08:00
n2 -> n3 [label="x"];
n3 [label="number"];
})",
toDot(*ty, opts));
}
2023-01-04 01:33:19 +08:00
TEST_CASE_FIXTURE(Fixture, "builtintypes")
2022-04-15 05:57:15 +08:00
{
CheckResult result = check(R"(
local x: "hi" | "\"hello\"" | true | false
)");
ToDotOptions opts;
opts.showPointers = false;
CHECK_EQ(R"(digraph graphname {
2023-01-04 01:33:19 +08:00
n1 [label="UnionType 1"];
2022-04-15 05:57:15 +08:00
n1 -> n2;
2023-01-04 01:33:19 +08:00
n2 [label="SingletonType string: hi"];
2022-04-15 05:57:15 +08:00
n1 -> n3;
)"
2023-01-04 01:33:19 +08:00
"n3 [label=\"SingletonType string: \\\"hello\\\"\"];"
2022-05-27 04:33:48 +08:00
R"(
2022-04-15 05:57:15 +08:00
n1 -> n4;
2023-01-04 01:33:19 +08:00
n4 [label="SingletonType boolean: true"];
2022-04-15 05:57:15 +08:00
n1 -> n5;
2023-01-04 01:33:19 +08:00
n5 [label="SingletonType boolean: false"];
2022-05-27 04:33:48 +08:00
})",
toDot(requireType("x"), opts));
2022-04-15 05:57:15 +08:00
}
2021-12-03 07:20:08 +08:00
TEST_SUITE_END();