diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md index ca7fff5731133..524ad383dc50c 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md @@ -9,8 +9,6 @@ from typing import Literal from enum import Enum mode: Literal["w", "r"] -mode2: Literal["w"] | Literal["r"] -union_var: Literal[Literal[Literal[1, 2, 3], "foo"], 5, None] a1: Literal[26] a2: Literal[0x1A] a3: Literal[-4] @@ -19,7 +17,6 @@ a5: Literal[b"hello world"] a6: Literal[True] a7: Literal[None] a8: Literal[Literal[1]] -a9: Literal[Literal["w"], Literal["r"], Literal[Literal["w+"]]] class Color(Enum): RED = 0 @@ -30,9 +27,6 @@ b1: Literal[Color.RED] def f(): reveal_type(mode) # revealed: Literal["w", "r"] - reveal_type(mode2) # revealed: Literal["w", "r"] - # TODO: should be revealed: Literal[1, 2, 3, "foo", 5] | None - reveal_type(union_var) # revealed: Literal[1, 2, 3, 5] | Literal["foo"] | None reveal_type(a1) # revealed: Literal[26] reveal_type(a2) # revealed: Literal[26] reveal_type(a3) # revealed: Literal[-4] @@ -41,7 +35,6 @@ def f(): reveal_type(a6) # revealed: Literal[True] reveal_type(a7) # revealed: None reveal_type(a8) # revealed: Literal[1] - reveal_type(a9) # revealed: Literal["w", "r", "w+"] # TODO: This should be Color.RED reveal_type(b1) # revealed: Literal[0] @@ -61,6 +54,63 @@ invalid4: Literal[ ] ``` +## Shortening unions of literals + +When a Literal is parameterized with more than one value, it’s treated as exactly to equivalent to +the union of those types. + +```py +from typing import Literal + +def x( + a1: Literal[Literal[Literal[1, 2, 3], "foo"], 5, None], + a2: Literal["w"] | Literal["r"], + a3: Literal[Literal["w"], Literal["r"], Literal[Literal["w+"]]], + a4: Literal[True] | Literal[1, 2] | Literal["foo"], +): + reveal_type(a1) # revealed: Literal[1, 2, 3, "foo", 5] | None + reveal_type(a2) # revealed: Literal["w", "r"] + reveal_type(a3) # revealed: Literal["w", "r", "w+"] + reveal_type(a4) # revealed: Literal[True, 1, 2, "foo"] +``` + +## Display of heterogeneous unions of literals + +```py +from typing import Literal, Union + +def foo(x: int) -> int: + return x + 1 + +def bar(s: str) -> str: + return s + +class A: ... +class B: ... + +def union_example( + x: Union[ + # unknown type + # error: [unresolved-reference] + y, + Literal[-1], + Literal["A"], + Literal[b"A"], + Literal[b"\x00"], + Literal[b"\x07"], + Literal[0], + Literal[1], + Literal["B"], + Literal["foo"], + Literal["bar"], + Literal["B"], + Literal[True], + None, + ] +): + reveal_type(x) # revealed: Unknown | Literal[-1, "A", b"A", b"\x00", b"\x07", 0, 1, "B", "foo", "bar", True] | None +``` + ## Detecting Literal outside typing and typing_extensions Only Literal that is defined in typing and typing_extension modules is detected as the special diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md index 16d5ac20c9def..b6f1e0ecdc87e 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal_string.md @@ -107,7 +107,7 @@ def _(flag: bool): qux_2: Literal["qux"] = baz_2 # error: [invalid-assignment] baz_3 = "foo" if flag else 1 - reveal_type(baz_3) # revealed: Literal["foo"] | Literal[1] + reveal_type(baz_3) # revealed: Literal["foo", 1] qux_3: LiteralString = baz_3 # error: [invalid-assignment] ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/string.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/string.md index 4ad2b7e4f3b12..af9f76a7557d3 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/string.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/string.md @@ -105,7 +105,7 @@ def f1( from typing import Literal def f(v: Literal["a", r"b", b"c", "d" "e", "\N{LATIN SMALL LETTER F}", "\x67", """h"""]): - reveal_type(v) # revealed: Literal["a", "b", "de", "f", "g", "h"] | Literal[b"c"] + reveal_type(v) # revealed: Literal["a", "b", b"c", "de", "f", "g", "h"] ``` ## Class variables diff --git a/crates/red_knot_python_semantic/resources/mdtest/call/union.md b/crates/red_knot_python_semantic/resources/mdtest/call/union.md index 55483854888af..e917bd42b0181 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/call/union.md +++ b/crates/red_knot_python_semantic/resources/mdtest/call/union.md @@ -56,7 +56,7 @@ def _(flag: bool, flag2: bool): else: def f() -> int: return 1 - # error: "Object of type `Literal[1] | Literal["foo"] | Literal[f]` is not callable (due to union elements Literal[1], Literal["foo"])" + # error: "Object of type `Literal[1, "foo"] | Literal[f]` is not callable (due to union elements Literal[1], Literal["foo"])" # revealed: Unknown | int reveal_type(f()) ``` @@ -72,6 +72,6 @@ def _(flag: bool): else: f = "foo" - x = f() # error: "Object of type `Literal[1] | Literal["foo"]` is not callable" + x = f() # error: "Object of type `Literal[1, "foo"]` is not callable" reveal_type(x) # revealed: Unknown ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/comparison/unsupported.md b/crates/red_knot_python_semantic/resources/mdtest/comparison/unsupported.md index 5dc769ac48020..eee53de4a1c1b 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/comparison/unsupported.md +++ b/crates/red_knot_python_semantic/resources/mdtest/comparison/unsupported.md @@ -22,7 +22,7 @@ def _(flag: bool, flag1: bool, flag2: bool): reveal_type(d) # revealed: bool int_literal_or_str_literal = 1 if flag else "foo" - # error: "Operator `in` is not supported for types `Literal[42]` and `Literal[1]`, in comparing `Literal[42]` with `Literal[1] | Literal["foo"]`" + # error: "Operator `in` is not supported for types `Literal[42]` and `Literal[1]`, in comparing `Literal[42]` with `Literal[1, "foo"]`" e = 42 in int_literal_or_str_literal reveal_type(e) # revealed: bool diff --git a/crates/red_knot_python_semantic/resources/mdtest/expression/attribute.md b/crates/red_knot_python_semantic/resources/mdtest/expression/attribute.md index 66f71e8dad398..b0872c17a930b 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/expression/attribute.md +++ b/crates/red_knot_python_semantic/resources/mdtest/expression/attribute.md @@ -17,7 +17,7 @@ def _(flag: bool): reveal_type(A.always_bound) # revealed: Literal[1] - reveal_type(A.union) # revealed: Literal[1] | Literal["abc"] + reveal_type(A.union) # revealed: Literal[1, "abc"] # error: [possibly-unbound-attribute] "Attribute `possibly_unbound` on type `Literal[A]` is possibly unbound" reveal_type(A.possibly_unbound) # revealed: Literal["abc"] diff --git a/crates/red_knot_python_semantic/resources/mdtest/expression/if.md b/crates/red_knot_python_semantic/resources/mdtest/expression/if.md index 6461522cefa46..d2c6131def818 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/expression/if.md +++ b/crates/red_knot_python_semantic/resources/mdtest/expression/if.md @@ -31,9 +31,9 @@ The test inside an if expression should not affect code outside of the expressio def _(flag: bool): x: Literal[42, "hello"] = 42 if flag else "hello" - reveal_type(x) # revealed: Literal[42] | Literal["hello"] + reveal_type(x) # revealed: Literal[42, "hello"] _ = ... if isinstance(x, str) else ... - reveal_type(x) # revealed: Literal[42] | Literal["hello"] + reveal_type(x) # revealed: Literal[42, "hello"] ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/expression/len.md b/crates/red_knot_python_semantic/resources/mdtest/expression/len.md index 3418adc77be47..ca1892c6a38ff 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/expression/len.md +++ b/crates/red_knot_python_semantic/resources/mdtest/expression/len.md @@ -119,7 +119,7 @@ class ZeroOrStr: reveal_type(len(Zero())) # revealed: Literal[0] reveal_type(len(ZeroOrOne())) # revealed: Literal[0, 1] reveal_type(len(ZeroOrTrue())) # revealed: Literal[0, 1] -reveal_type(len(OneOrFalse())) # revealed: Literal[0, 1] +reveal_type(len(OneOrFalse())) # revealed: Literal[1, 0] # TODO: Emit a diagnostic reveal_type(len(OneOrFoo())) # revealed: int diff --git a/crates/red_knot_python_semantic/resources/mdtest/loops/for.md b/crates/red_knot_python_semantic/resources/mdtest/loops/for.md index 58675475abe72..791ab7b1bd704 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/loops/for.md +++ b/crates/red_knot_python_semantic/resources/mdtest/loops/for.md @@ -98,7 +98,7 @@ reveal_type(x) for x in (1, "a", b"foo"): pass -# revealed: Literal[1] | Literal["a"] | Literal[b"foo"] +# revealed: Literal[1, "a", b"foo"] # error: [possibly-unresolved-reference] reveal_type(x) ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/loops/while_loop.md b/crates/red_knot_python_semantic/resources/mdtest/loops/while_loop.md index 7885d08638b8e..e30297f90536a 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/loops/while_loop.md +++ b/crates/red_knot_python_semantic/resources/mdtest/loops/while_loop.md @@ -41,7 +41,7 @@ def _(flag: bool, flag2: bool): x = 3 reveal_type(x) # revealed: Literal[2, 3] - reveal_type(y) # revealed: Literal[1, 2, 4] + reveal_type(y) # revealed: Literal[4, 1, 2] ``` ## Nested `while` loops diff --git a/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals/is.md b/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals/is.md index c0aa140b408d3..f5d430f7d1abb 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals/is.md +++ b/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals/is.md @@ -56,7 +56,7 @@ def _(x_flag: bool, y_flag: bool): def _(flag1: bool, flag2: bool): x = None if flag1 else (1 if flag2 else True) - reveal_type(x) # revealed: None | Literal[1] | Literal[True] + reveal_type(x) # revealed: None | Literal[1, True] if x is None: reveal_type(x) # revealed: None elif x is True: diff --git a/crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md b/crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md index 3c1e6730f475b..88c05617c6035 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md +++ b/crates/red_knot_python_semantic/resources/mdtest/narrow/isinstance.md @@ -17,7 +17,7 @@ def _(flag: bool): reveal_type(x) # revealed: Never if isinstance(x, (int, object)): - reveal_type(x) # revealed: Literal[1] | Literal["a"] + reveal_type(x) # revealed: Literal[1, "a"] ``` ## `classinfo` is a tuple of types @@ -30,7 +30,7 @@ def _(flag: bool, flag1: bool, flag2: bool): x = 1 if flag else "a" if isinstance(x, (int, str)): - reveal_type(x) # revealed: Literal[1] | Literal["a"] + reveal_type(x) # revealed: Literal[1, "a"] else: reveal_type(x) # revealed: Never @@ -43,19 +43,19 @@ def _(flag: bool, flag1: bool, flag2: bool): # No narrowing should occur if a larger type is also # one of the possibilities: if isinstance(x, (int, object)): - reveal_type(x) # revealed: Literal[1] | Literal["a"] + reveal_type(x) # revealed: Literal[1, "a"] else: reveal_type(x) # revealed: Never y = 1 if flag1 else "a" if flag2 else b"b" if isinstance(y, (int, str)): - reveal_type(y) # revealed: Literal[1] | Literal["a"] + reveal_type(y) # revealed: Literal[1, "a"] if isinstance(y, (int, bytes)): - reveal_type(y) # revealed: Literal[1] | Literal[b"b"] + reveal_type(y) # revealed: Literal[1, b"b"] if isinstance(y, (str, bytes)): - reveal_type(y) # revealed: Literal["a"] | Literal[b"b"] + reveal_type(y) # revealed: Literal["a", b"b"] ``` ## `classinfo` is a nested tuple of types @@ -107,7 +107,7 @@ def _(flag: bool): x = 1 if flag else "foo" if isinstance(x, t): - reveal_type(x) # revealed: Literal[1] | Literal["foo"] + reveal_type(x) # revealed: Literal[1, "foo"] ``` ## Do not use custom `isinstance` for narrowing @@ -119,7 +119,7 @@ def _(flag: bool): x = 1 if flag else "a" if isinstance(x, int): - reveal_type(x) # revealed: Literal[1] | Literal["a"] + reveal_type(x) # revealed: Literal[1, "a"] ``` ## Do support narrowing if `isinstance` is aliased @@ -155,12 +155,12 @@ def _(flag: bool): # TODO: this should cause us to emit a diagnostic during # type checking if isinstance(x, "a"): - reveal_type(x) # revealed: Literal[1] | Literal["a"] + reveal_type(x) # revealed: Literal[1, "a"] # TODO: this should cause us to emit a diagnostic during # type checking if isinstance(x, "int"): - reveal_type(x) # revealed: Literal[1] | Literal["a"] + reveal_type(x) # revealed: Literal[1, "a"] ``` ## Do not narrow if there are keyword arguments @@ -171,7 +171,7 @@ def _(flag: bool): # error: [unknown-argument] if isinstance(x, int, foo="bar"): - reveal_type(x) # revealed: Literal[1] | Literal["a"] + reveal_type(x) # revealed: Literal[1, "a"] ``` ## `type[]` types are narrowed as well as class-literal types diff --git a/crates/red_knot_python_semantic/resources/mdtest/narrow/truthiness.md b/crates/red_knot_python_semantic/resources/mdtest/narrow/truthiness.md index 80c4a90831d4e..5cf287329a43d 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/narrow/truthiness.md +++ b/crates/red_knot_python_semantic/resources/mdtest/narrow/truthiness.md @@ -9,39 +9,39 @@ def foo() -> Literal[0, -1, True, False, "", "foo", b"", b"bar", None] | tuple[( x = foo() if x: - reveal_type(x) # revealed: Literal[-1] | Literal[True] | Literal["foo"] | Literal[b"bar"] + reveal_type(x) # revealed: Literal[-1, True, "foo", b"bar"] else: - reveal_type(x) # revealed: Literal[0] | Literal[False] | Literal[""] | Literal[b""] | None | tuple[()] + reveal_type(x) # revealed: Literal[0, False, "", b""] | None | tuple[()] if not x: - reveal_type(x) # revealed: Literal[0] | Literal[False] | Literal[""] | Literal[b""] | None | tuple[()] + reveal_type(x) # revealed: Literal[0, False, "", b""] | None | tuple[()] else: - reveal_type(x) # revealed: Literal[-1] | Literal[True] | Literal["foo"] | Literal[b"bar"] + reveal_type(x) # revealed: Literal[-1, True, "foo", b"bar"] if x and not x: reveal_type(x) # revealed: Never else: - reveal_type(x) # revealed: Literal[-1, 0] | bool | Literal["", "foo"] | Literal[b"", b"bar"] | None | tuple[()] + reveal_type(x) # revealed: Literal[0, "", b"", -1, "foo", b"bar"] | bool | None | tuple[()] if not (x and not x): - reveal_type(x) # revealed: Literal[-1, 0] | bool | Literal["", "foo"] | Literal[b"", b"bar"] | None | tuple[()] + reveal_type(x) # revealed: Literal[0, "", b"", -1, "foo", b"bar"] | bool | None | tuple[()] else: reveal_type(x) # revealed: Never if x or not x: - reveal_type(x) # revealed: Literal[-1, 0] | bool | Literal["foo", ""] | Literal[b"bar", b""] | None | tuple[()] + reveal_type(x) # revealed: Literal[-1, "foo", b"bar", 0, "", b""] | bool | None | tuple[()] else: reveal_type(x) # revealed: Never if not (x or not x): reveal_type(x) # revealed: Never else: - reveal_type(x) # revealed: Literal[-1, 0] | bool | Literal["foo", ""] | Literal[b"bar", b""] | None | tuple[()] + reveal_type(x) # revealed: Literal[-1, "foo", b"bar", 0, "", b""] | bool | None | tuple[()] if (isinstance(x, int) or isinstance(x, str)) and x: - reveal_type(x) # revealed: Literal[-1] | Literal[True] | Literal["foo"] + reveal_type(x) # revealed: Literal[-1, True, "foo"] else: - reveal_type(x) # revealed: Literal[b"", b"bar"] | None | tuple[()] | Literal[0] | Literal[False] | Literal[""] + reveal_type(x) # revealed: Literal[b"", b"bar", 0, False, ""] | None | tuple[()] ``` ## Function Literals @@ -166,16 +166,16 @@ y = literals() if isinstance(x, str) and not isinstance(x, B): reveal_type(x) # revealed: A & str & ~B - reveal_type(y) # revealed: Literal[0, 42] | Literal["", "hello"] + reveal_type(y) # revealed: Literal[0, 42, "", "hello"] z = x if flag() else y - reveal_type(z) # revealed: A & str & ~B | Literal[0, 42] | Literal["", "hello"] + reveal_type(z) # revealed: A & str & ~B | Literal[0, 42, "", "hello"] if z: - reveal_type(z) # revealed: A & str & ~B & ~AlwaysFalsy | Literal[42] | Literal["hello"] + reveal_type(z) # revealed: A & str & ~B & ~AlwaysFalsy | Literal[42, "hello"] else: - reveal_type(z) # revealed: A & str & ~B & ~AlwaysTruthy | Literal[0] | Literal[""] + reveal_type(z) # revealed: A & str & ~B & ~AlwaysTruthy | Literal[0, ""] ``` ## Narrowing Multiple Variables diff --git a/crates/red_knot_python_semantic/resources/mdtest/scopes/unbound.md b/crates/red_knot_python_semantic/resources/mdtest/scopes/unbound.md index 0a50dee2b22ca..3030865c9d0ff 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/scopes/unbound.md +++ b/crates/red_knot_python_semantic/resources/mdtest/scopes/unbound.md @@ -37,7 +37,7 @@ class C: # error: [possibly-unresolved-reference] y = x -reveal_type(C.y) # revealed: Literal[1] | Literal["abc"] +reveal_type(C.y) # revealed: Literal[1, "abc"] ``` ## Unbound function local diff --git a/crates/red_knot_python_semantic/resources/mdtest/unpacking.md b/crates/red_knot_python_semantic/resources/mdtest/unpacking.md index 6ea2a9b276f09..f962c42837a6f 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/unpacking.md +++ b/crates/red_knot_python_semantic/resources/mdtest/unpacking.md @@ -426,8 +426,8 @@ def _(flag: bool): value = ("a", "b") a, b = value - reveal_type(a) # revealed: Literal[1] | Literal["a"] - reveal_type(b) # revealed: Literal[2] | Literal["b"] + reveal_type(a) # revealed: Literal[1, "a"] + reveal_type(b) # revealed: Literal[2, "b"] ``` ### Typing literal @@ -528,8 +528,8 @@ for a, b in ((1, 2), (3, 4)): ```py for a, b in ((1, 2), ("a", "b")): - reveal_type(a) # revealed: Literal[1] | Literal["a"] - reveal_type(b) # revealed: Literal[2] | Literal["b"] + reveal_type(a) # revealed: Literal[1, "a"] + reveal_type(b) # revealed: Literal[2, "b"] ``` ### Mixed literals values (2) diff --git a/crates/red_knot_python_semantic/src/types/display.rs b/crates/red_knot_python_semantic/src/types/display.rs index ded048b07c63c..b0f49c4441bfd 100644 --- a/crates/red_knot_python_semantic/src/types/display.rs +++ b/crates/red_knot_python_semantic/src/types/display.rs @@ -175,12 +175,9 @@ impl Display for DisplayUnionType<'_> { for element in elements { if let Ok(kind) = CondensedDisplayTypeKind::try_from(*element) { - let Some(mut condensed_kind) = grouped_condensed_kinds.remove(&kind) else { + let Some(condensed_kind) = grouped_condensed_kinds.remove(&kind) else { continue; }; - if kind == CondensedDisplayTypeKind::Int { - condensed_kind.sort_unstable_by_key(|ty| ty.expect_int_literal()); - } join.entry(&DisplayLiteralGroup { literals: condensed_kind, db: self.db, @@ -221,17 +218,12 @@ impl Display for DisplayLiteralGroup<'_> { /// Enumeration of literal types that are displayed in a "condensed way" inside `Literal` slices. /// -/// For example, `Literal[1] | Literal[2]` is displayed as `"Literal[1, 2]"`. -/// Not all `Literal` types are displayed using `Literal` slices -/// (e.g. it would be inappropriate to display `LiteralString` -/// as `Literal[LiteralString]`). +/// For example, `Literal[1] | Literal[2] | Literal["s"]` is displayed as `"Literal[1, 2, "s"]"`. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] enum CondensedDisplayTypeKind { Class, Function, - Int, - String, - Bytes, + LiteralExpression, } impl TryFrom> for CondensedDisplayTypeKind { @@ -241,9 +233,10 @@ impl TryFrom> for CondensedDisplayTypeKind { match value { Type::ClassLiteral(_) => Ok(Self::Class), Type::FunctionLiteral(_) => Ok(Self::Function), - Type::IntLiteral(_) => Ok(Self::Int), - Type::StringLiteral(_) => Ok(Self::String), - Type::BytesLiteral(_) => Ok(Self::Bytes), + Type::IntLiteral(_) + | Type::StringLiteral(_) + | Type::BytesLiteral(_) + | Type::BooleanLiteral(_) => Ok(Self::LiteralExpression), _ => Err(()), } } @@ -370,64 +363,8 @@ impl Display for DisplayStringLiteralType<'_> { #[cfg(test)] mod tests { - use ruff_db::files::system_path_to_file; - use ruff_db::system::DbWithTestSystem; - use crate::db::tests::setup_db; - use crate::types::{global_symbol, SliceLiteralType, StringLiteralType, Type, UnionType}; - - #[test] - fn test_condense_literal_display_by_type() -> anyhow::Result<()> { - let mut db = setup_db(); - - db.write_dedented( - "src/main.py", - " - def foo(x: int) -> int: - return x + 1 - - def bar(s: str) -> str: - return s - - class A: ... - class B: ... - ", - )?; - let mod_file = system_path_to_file(&db, "src/main.py").expect("file to exist"); - - let union_elements = &[ - Type::Unknown, - Type::IntLiteral(-1), - global_symbol(&db, mod_file, "A").expect_type(), - Type::string_literal(&db, "A"), - Type::bytes_literal(&db, &[0u8]), - Type::bytes_literal(&db, &[7u8]), - Type::IntLiteral(0), - Type::IntLiteral(1), - Type::string_literal(&db, "B"), - global_symbol(&db, mod_file, "foo").expect_type(), - global_symbol(&db, mod_file, "bar").expect_type(), - global_symbol(&db, mod_file, "B").expect_type(), - Type::BooleanLiteral(true), - Type::none(&db), - ]; - let union = UnionType::from_elements(&db, union_elements).expect_union(); - let display = format!("{}", union.display(&db)); - assert_eq!( - display, - concat!( - "Unknown | ", - "Literal[-1, 0, 1] | ", - "Literal[A, B] | ", - "Literal[\"A\", \"B\"] | ", - "Literal[b\"\\x00\", b\"\\x07\"] | ", - "Literal[foo, bar] | ", - "Literal[True] | ", - "None" - ) - ); - Ok(()) - } + use crate::types::{SliceLiteralType, StringLiteralType, Type}; #[test] fn test_slice_literal_display() {