Tipragot
628be439b8
Cela permet de ne pas avoir de problèmes de compatibilité car python est dans le git.
374 lines
14 KiB
Python
374 lines
14 KiB
Python
"""Test cases for type inference helper functions."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from mypy.argmap import map_actuals_to_formals
|
|
from mypy.checker import DisjointDict, group_comparison_operands
|
|
from mypy.literals import Key
|
|
from mypy.nodes import ARG_NAMED, ARG_OPT, ARG_POS, ARG_STAR, ARG_STAR2, ArgKind, NameExpr
|
|
from mypy.test.helpers import Suite, assert_equal
|
|
from mypy.test.typefixture import TypeFixture
|
|
from mypy.types import AnyType, TupleType, Type, TypeOfAny
|
|
|
|
|
|
class MapActualsToFormalsSuite(Suite):
|
|
"""Test cases for argmap.map_actuals_to_formals."""
|
|
|
|
def test_basic(self) -> None:
|
|
self.assert_map([], [], [])
|
|
|
|
def test_positional_only(self) -> None:
|
|
self.assert_map([ARG_POS], [ARG_POS], [[0]])
|
|
self.assert_map([ARG_POS, ARG_POS], [ARG_POS, ARG_POS], [[0], [1]])
|
|
|
|
def test_optional(self) -> None:
|
|
self.assert_map([], [ARG_OPT], [[]])
|
|
self.assert_map([ARG_POS], [ARG_OPT], [[0]])
|
|
self.assert_map([ARG_POS], [ARG_OPT, ARG_OPT], [[0], []])
|
|
|
|
def test_callee_star(self) -> None:
|
|
self.assert_map([], [ARG_STAR], [[]])
|
|
self.assert_map([ARG_POS], [ARG_STAR], [[0]])
|
|
self.assert_map([ARG_POS, ARG_POS], [ARG_STAR], [[0, 1]])
|
|
|
|
def test_caller_star(self) -> None:
|
|
self.assert_map([ARG_STAR], [ARG_STAR], [[0]])
|
|
self.assert_map([ARG_POS, ARG_STAR], [ARG_STAR], [[0, 1]])
|
|
self.assert_map([ARG_STAR], [ARG_POS, ARG_STAR], [[0], [0]])
|
|
self.assert_map([ARG_STAR], [ARG_OPT, ARG_STAR], [[0], [0]])
|
|
|
|
def test_too_many_caller_args(self) -> None:
|
|
self.assert_map([ARG_POS], [], [])
|
|
self.assert_map([ARG_STAR], [], [])
|
|
self.assert_map([ARG_STAR], [ARG_POS], [[0]])
|
|
|
|
def test_tuple_star(self) -> None:
|
|
any_type = AnyType(TypeOfAny.special_form)
|
|
self.assert_vararg_map([ARG_STAR], [ARG_POS], [[0]], self.make_tuple(any_type))
|
|
self.assert_vararg_map(
|
|
[ARG_STAR], [ARG_POS, ARG_POS], [[0], [0]], self.make_tuple(any_type, any_type)
|
|
)
|
|
self.assert_vararg_map(
|
|
[ARG_STAR],
|
|
[ARG_POS, ARG_OPT, ARG_OPT],
|
|
[[0], [0], []],
|
|
self.make_tuple(any_type, any_type),
|
|
)
|
|
|
|
def make_tuple(self, *args: Type) -> TupleType:
|
|
return TupleType(list(args), TypeFixture().std_tuple)
|
|
|
|
def test_named_args(self) -> None:
|
|
self.assert_map(["x"], [(ARG_POS, "x")], [[0]])
|
|
self.assert_map(["y", "x"], [(ARG_POS, "x"), (ARG_POS, "y")], [[1], [0]])
|
|
|
|
def test_some_named_args(self) -> None:
|
|
self.assert_map(["y"], [(ARG_OPT, "x"), (ARG_OPT, "y"), (ARG_OPT, "z")], [[], [0], []])
|
|
|
|
def test_missing_named_arg(self) -> None:
|
|
self.assert_map(["y"], [(ARG_OPT, "x")], [[]])
|
|
|
|
def test_duplicate_named_arg(self) -> None:
|
|
self.assert_map(["x", "x"], [(ARG_OPT, "x")], [[0, 1]])
|
|
|
|
def test_varargs_and_bare_asterisk(self) -> None:
|
|
self.assert_map([ARG_STAR], [ARG_STAR, (ARG_NAMED, "x")], [[0], []])
|
|
self.assert_map([ARG_STAR, "x"], [ARG_STAR, (ARG_NAMED, "x")], [[0], [1]])
|
|
|
|
def test_keyword_varargs(self) -> None:
|
|
self.assert_map(["x"], [ARG_STAR2], [[0]])
|
|
self.assert_map(["x", ARG_STAR2], [ARG_STAR2], [[0, 1]])
|
|
self.assert_map(["x", ARG_STAR2], [(ARG_POS, "x"), ARG_STAR2], [[0], [1]])
|
|
self.assert_map([ARG_POS, ARG_STAR2], [(ARG_POS, "x"), ARG_STAR2], [[0], [1]])
|
|
|
|
def test_both_kinds_of_varargs(self) -> None:
|
|
self.assert_map([ARG_STAR, ARG_STAR2], [(ARG_POS, "x"), (ARG_POS, "y")], [[0, 1], [0, 1]])
|
|
|
|
def test_special_cases(self) -> None:
|
|
self.assert_map([ARG_STAR], [ARG_STAR, ARG_STAR2], [[0], []])
|
|
self.assert_map([ARG_STAR, ARG_STAR2], [ARG_STAR, ARG_STAR2], [[0], [1]])
|
|
self.assert_map([ARG_STAR2], [(ARG_POS, "x"), ARG_STAR2], [[0], [0]])
|
|
self.assert_map([ARG_STAR2], [ARG_STAR2], [[0]])
|
|
|
|
def assert_map(
|
|
self,
|
|
caller_kinds_: list[ArgKind | str],
|
|
callee_kinds_: list[ArgKind | tuple[ArgKind, str]],
|
|
expected: list[list[int]],
|
|
) -> None:
|
|
caller_kinds, caller_names = expand_caller_kinds(caller_kinds_)
|
|
callee_kinds, callee_names = expand_callee_kinds(callee_kinds_)
|
|
result = map_actuals_to_formals(
|
|
caller_kinds,
|
|
caller_names,
|
|
callee_kinds,
|
|
callee_names,
|
|
lambda i: AnyType(TypeOfAny.special_form),
|
|
)
|
|
assert_equal(result, expected)
|
|
|
|
def assert_vararg_map(
|
|
self,
|
|
caller_kinds: list[ArgKind],
|
|
callee_kinds: list[ArgKind],
|
|
expected: list[list[int]],
|
|
vararg_type: Type,
|
|
) -> None:
|
|
result = map_actuals_to_formals(caller_kinds, [], callee_kinds, [], lambda i: vararg_type)
|
|
assert_equal(result, expected)
|
|
|
|
|
|
def expand_caller_kinds(
|
|
kinds_or_names: list[ArgKind | str],
|
|
) -> tuple[list[ArgKind], list[str | None]]:
|
|
kinds = []
|
|
names: list[str | None] = []
|
|
for k in kinds_or_names:
|
|
if isinstance(k, str):
|
|
kinds.append(ARG_NAMED)
|
|
names.append(k)
|
|
else:
|
|
kinds.append(k)
|
|
names.append(None)
|
|
return kinds, names
|
|
|
|
|
|
def expand_callee_kinds(
|
|
kinds_and_names: list[ArgKind | tuple[ArgKind, str]]
|
|
) -> tuple[list[ArgKind], list[str | None]]:
|
|
kinds = []
|
|
names: list[str | None] = []
|
|
for v in kinds_and_names:
|
|
if isinstance(v, tuple):
|
|
kinds.append(v[0])
|
|
names.append(v[1])
|
|
else:
|
|
kinds.append(v)
|
|
names.append(None)
|
|
return kinds, names
|
|
|
|
|
|
class OperandDisjointDictSuite(Suite):
|
|
"""Test cases for checker.DisjointDict, which is used for type inference with operands."""
|
|
|
|
def new(self) -> DisjointDict[int, str]:
|
|
return DisjointDict()
|
|
|
|
def test_independent_maps(self) -> None:
|
|
d = self.new()
|
|
d.add_mapping({0, 1}, {"group1"})
|
|
d.add_mapping({2, 3, 4}, {"group2"})
|
|
d.add_mapping({5, 6, 7}, {"group3"})
|
|
|
|
self.assertEqual(
|
|
d.items(), [({0, 1}, {"group1"}), ({2, 3, 4}, {"group2"}), ({5, 6, 7}, {"group3"})]
|
|
)
|
|
|
|
def test_partial_merging(self) -> None:
|
|
d = self.new()
|
|
d.add_mapping({0, 1}, {"group1"})
|
|
d.add_mapping({1, 2}, {"group2"})
|
|
d.add_mapping({3, 4}, {"group3"})
|
|
d.add_mapping({5, 0}, {"group4"})
|
|
d.add_mapping({5, 6}, {"group5"})
|
|
d.add_mapping({4, 7}, {"group6"})
|
|
|
|
self.assertEqual(
|
|
d.items(),
|
|
[
|
|
({0, 1, 2, 5, 6}, {"group1", "group2", "group4", "group5"}),
|
|
({3, 4, 7}, {"group3", "group6"}),
|
|
],
|
|
)
|
|
|
|
def test_full_merging(self) -> None:
|
|
d = self.new()
|
|
d.add_mapping({0, 1, 2}, {"a"})
|
|
d.add_mapping({3, 4, 2}, {"b"})
|
|
d.add_mapping({10, 11, 12}, {"c"})
|
|
d.add_mapping({13, 14, 15}, {"d"})
|
|
d.add_mapping({14, 10, 16}, {"e"})
|
|
d.add_mapping({0, 10}, {"f"})
|
|
|
|
self.assertEqual(
|
|
d.items(),
|
|
[({0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 15, 16}, {"a", "b", "c", "d", "e", "f"})],
|
|
)
|
|
|
|
def test_merge_with_multiple_overlaps(self) -> None:
|
|
d = self.new()
|
|
d.add_mapping({0, 1, 2}, {"a"})
|
|
d.add_mapping({3, 4, 5}, {"b"})
|
|
d.add_mapping({1, 2, 4, 5}, {"c"})
|
|
d.add_mapping({6, 1, 2, 4, 5}, {"d"})
|
|
d.add_mapping({6, 1, 2, 4, 5}, {"e"})
|
|
|
|
self.assertEqual(d.items(), [({0, 1, 2, 3, 4, 5, 6}, {"a", "b", "c", "d", "e"})])
|
|
|
|
|
|
class OperandComparisonGroupingSuite(Suite):
|
|
"""Test cases for checker.group_comparison_operands."""
|
|
|
|
def literal_keymap(self, assignable_operands: dict[int, NameExpr]) -> dict[int, Key]:
|
|
output: dict[int, Key] = {}
|
|
for index, expr in assignable_operands.items():
|
|
output[index] = ("FakeExpr", expr.name)
|
|
return output
|
|
|
|
def test_basic_cases(self) -> None:
|
|
# Note: the grouping function doesn't actually inspect the input exprs, so we
|
|
# just default to using NameExprs for simplicity.
|
|
x0 = NameExpr("x0")
|
|
x1 = NameExpr("x1")
|
|
x2 = NameExpr("x2")
|
|
x3 = NameExpr("x3")
|
|
x4 = NameExpr("x4")
|
|
|
|
basic_input = [("==", x0, x1), ("==", x1, x2), ("<", x2, x3), ("==", x3, x4)]
|
|
|
|
none_assignable = self.literal_keymap({})
|
|
all_assignable = self.literal_keymap({0: x0, 1: x1, 2: x2, 3: x3, 4: x4})
|
|
|
|
for assignable in [none_assignable, all_assignable]:
|
|
self.assertEqual(
|
|
group_comparison_operands(basic_input, assignable, set()),
|
|
[("==", [0, 1]), ("==", [1, 2]), ("<", [2, 3]), ("==", [3, 4])],
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(basic_input, assignable, {"=="}),
|
|
[("==", [0, 1, 2]), ("<", [2, 3]), ("==", [3, 4])],
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(basic_input, assignable, {"<"}),
|
|
[("==", [0, 1]), ("==", [1, 2]), ("<", [2, 3]), ("==", [3, 4])],
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(basic_input, assignable, {"==", "<"}),
|
|
[("==", [0, 1, 2]), ("<", [2, 3]), ("==", [3, 4])],
|
|
)
|
|
|
|
def test_multiple_groups(self) -> None:
|
|
x0 = NameExpr("x0")
|
|
x1 = NameExpr("x1")
|
|
x2 = NameExpr("x2")
|
|
x3 = NameExpr("x3")
|
|
x4 = NameExpr("x4")
|
|
x5 = NameExpr("x5")
|
|
|
|
self.assertEqual(
|
|
group_comparison_operands(
|
|
[("==", x0, x1), ("==", x1, x2), ("is", x2, x3), ("is", x3, x4)],
|
|
self.literal_keymap({}),
|
|
{"==", "is"},
|
|
),
|
|
[("==", [0, 1, 2]), ("is", [2, 3, 4])],
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(
|
|
[("==", x0, x1), ("==", x1, x2), ("==", x2, x3), ("==", x3, x4)],
|
|
self.literal_keymap({}),
|
|
{"==", "is"},
|
|
),
|
|
[("==", [0, 1, 2, 3, 4])],
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(
|
|
[("is", x0, x1), ("==", x1, x2), ("==", x2, x3), ("==", x3, x4)],
|
|
self.literal_keymap({}),
|
|
{"==", "is"},
|
|
),
|
|
[("is", [0, 1]), ("==", [1, 2, 3, 4])],
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(
|
|
[("is", x0, x1), ("is", x1, x2), ("<", x2, x3), ("==", x3, x4), ("==", x4, x5)],
|
|
self.literal_keymap({}),
|
|
{"==", "is"},
|
|
),
|
|
[("is", [0, 1, 2]), ("<", [2, 3]), ("==", [3, 4, 5])],
|
|
)
|
|
|
|
def test_multiple_groups_coalescing(self) -> None:
|
|
x0 = NameExpr("x0")
|
|
x1 = NameExpr("x1")
|
|
x2 = NameExpr("x2")
|
|
x3 = NameExpr("x3")
|
|
x4 = NameExpr("x4")
|
|
|
|
nothing_combined = [("==", [0, 1, 2]), ("<", [2, 3]), ("==", [3, 4, 5])]
|
|
everything_combined = [("==", [0, 1, 2, 3, 4, 5]), ("<", [2, 3])]
|
|
|
|
# Note: We do 'x4 == x0' at the very end!
|
|
two_groups = [
|
|
("==", x0, x1),
|
|
("==", x1, x2),
|
|
("<", x2, x3),
|
|
("==", x3, x4),
|
|
("==", x4, x0),
|
|
]
|
|
self.assertEqual(
|
|
group_comparison_operands(
|
|
two_groups, self.literal_keymap({0: x0, 1: x1, 2: x2, 3: x3, 4: x4, 5: x0}), {"=="}
|
|
),
|
|
everything_combined,
|
|
"All vars are assignable, everything is combined",
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(
|
|
two_groups, self.literal_keymap({1: x1, 2: x2, 3: x3, 4: x4}), {"=="}
|
|
),
|
|
nothing_combined,
|
|
"x0 is unassignable, so no combining",
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(
|
|
two_groups, self.literal_keymap({0: x0, 1: x1, 3: x3, 5: x0}), {"=="}
|
|
),
|
|
everything_combined,
|
|
"Some vars are unassignable but x0 is, so we combine",
|
|
)
|
|
self.assertEqual(
|
|
group_comparison_operands(two_groups, self.literal_keymap({0: x0, 5: x0}), {"=="}),
|
|
everything_combined,
|
|
"All vars are unassignable but x0 is, so we combine",
|
|
)
|
|
|
|
def test_multiple_groups_different_operators(self) -> None:
|
|
x0 = NameExpr("x0")
|
|
x1 = NameExpr("x1")
|
|
x2 = NameExpr("x2")
|
|
x3 = NameExpr("x3")
|
|
|
|
groups = [("==", x0, x1), ("==", x1, x2), ("is", x2, x3), ("is", x3, x0)]
|
|
keymap = self.literal_keymap({0: x0, 1: x1, 2: x2, 3: x3, 4: x0})
|
|
self.assertEqual(
|
|
group_comparison_operands(groups, keymap, {"==", "is"}),
|
|
[("==", [0, 1, 2]), ("is", [2, 3, 4])],
|
|
"Different operators can never be combined",
|
|
)
|
|
|
|
def test_single_pair(self) -> None:
|
|
x0 = NameExpr("x0")
|
|
x1 = NameExpr("x1")
|
|
|
|
single_comparison = [("==", x0, x1)]
|
|
expected_output = [("==", [0, 1])]
|
|
|
|
assignable_combinations: list[dict[int, NameExpr]] = [{}, {0: x0}, {1: x1}, {0: x0, 1: x1}]
|
|
to_group_by: list[set[str]] = [set(), {"=="}, {"is"}]
|
|
|
|
for combo in assignable_combinations:
|
|
for operators in to_group_by:
|
|
keymap = self.literal_keymap(combo)
|
|
self.assertEqual(
|
|
group_comparison_operands(single_comparison, keymap, operators),
|
|
expected_output,
|
|
)
|
|
|
|
def test_empty_pair_list(self) -> None:
|
|
# This case should never occur in practice -- ComparisionExprs
|
|
# always contain at least one comparison. But in case it does...
|
|
|
|
self.assertEqual(group_comparison_operands([], {}, set()), [])
|
|
self.assertEqual(group_comparison_operands([], {}, {"=="}), [])
|