11from __future__ import annotations
22
33from mypy .nodes import CONTRAVARIANT , COVARIANT , INVARIANT
4- from mypy .subtypes import is_subtype
4+ from mypy .subtypes import is_proper_subtype , is_subtype , restrict_subtype_away
55from mypy .test .helpers import Suite
66from mypy .test .typefixture import InterfaceTypeFixture , TypeFixture
77from mypy .types import Instance , TupleType , Type , UninhabitedType , UnpackType
@@ -277,6 +277,74 @@ def test_type_var_tuple_unpacked_variable_length_tuple(self) -> None:
277277 def test_fallback_not_subtype_of_tuple (self ) -> None :
278278 self .assert_not_subtype (self .fx .a , TupleType ([self .fx .b ], fallback = self .fx .a ))
279279
280+ def test_literal (self ) -> None :
281+ str1 = self .fx .lit_str1
282+ str2 = self .fx .lit_str2
283+ str1_inst = self .fx .lit_str1_inst
284+ str2_inst = self .fx .lit_str2_inst
285+ str_type = self .fx .str_type
286+
287+ # other operand is the fallback type
288+ # "x" ≲ str -> YES
289+ # str ≲ "x" -> NO
290+ # "x"? ≲ str -> YES
291+ # str ≲ "x"? -> YES
292+ self .assert_subtype (str1 , str_type )
293+ self .assert_not_subtype (str_type , str1 )
294+ self .assert_subtype (str1_inst , str_type )
295+ self .assert_subtype (str_type , str1_inst )
296+
297+ # other operand is the same literal
298+ # "x" ≲ "x" -> YES
299+ # "x" ≲ "x"? -> YES
300+ # "x"? ≲ "x" -> YES
301+ # "x"? ≲ "x"? -> YES
302+ self .assert_subtype (str1 , str1 )
303+ self .assert_subtype (str1 , str1_inst )
304+ self .assert_subtype (str1_inst , str1 )
305+ self .assert_subtype (str1_inst , str1_inst )
306+
307+ # second operand is a different literal
308+ # "x" ≲ "y" -> NO
309+ # "x" ≲ "y"? -> YES
310+ # "x"? ≲ "y" -> NO
311+ # "x"? ≲ "y"? -> YES
312+ self .assert_not_subtype (str1 , str2 )
313+ self .assert_subtype (str1 , str2_inst )
314+ self .assert_not_subtype (str1_inst , str2 )
315+ self .assert_subtype (str1_inst , str2_inst )
316+
317+ # check proper subtyping
318+ # second operand is the fallback type
319+ # "x" <: str -> YES
320+ # str <: "x" -> NO
321+ # "x"? <: str -> YES
322+ # str <: "x"? -> YES
323+ self .assert_proper_subtype (str1 , str_type )
324+ self .assert_not_proper_subtype (str_type , str1 )
325+ self .assert_proper_subtype (str1_inst , str_type )
326+ self .assert_proper_subtype (str_type , str1_inst )
327+
328+ # second operand is the same literal
329+ # "x" <: "x" -> YES
330+ # "x" <: "x"? -> YES
331+ # "x"? <: "x" -> NO
332+ # "x"? <: "x"? -> YES
333+ self .assert_proper_subtype (str1 , str1 )
334+ self .assert_proper_subtype (str1 , str1_inst )
335+ self .assert_not_proper_subtype (str1_inst , str1 )
336+ self .assert_proper_subtype (str1_inst , str1_inst )
337+
338+ # second operand is a different literal
339+ # "x" ≲ "y" -> NO
340+ # "x" ≲ "y"? -> NO
341+ # "x"? ≲ "y" -> NO
342+ # "x"? ≲ "y"? -> YES
343+ self .assert_not_proper_subtype (str1 , str2 )
344+ self .assert_not_proper_subtype (str1 , str2_inst )
345+ self .assert_not_proper_subtype (str1_inst , str2 )
346+ self .assert_proper_subtype (str1_inst , str2_inst )
347+
280348 # IDEA: Maybe add these test cases (they are tested pretty well in type
281349 # checker tests already):
282350 # * more interface subtyping test cases
@@ -287,6 +355,12 @@ def test_fallback_not_subtype_of_tuple(self) -> None:
287355 # * any type
288356 # * generic function types
289357
358+ def assert_proper_subtype (self , s : Type , t : Type ) -> None :
359+ assert is_proper_subtype (s , t ), f"{ s } not proper subtype of { t } "
360+
361+ def assert_not_proper_subtype (self , s : Type , t : Type ) -> None :
362+ assert not is_proper_subtype (s , t ), f"{ s } not proper subtype of { t } "
363+
290364 def assert_subtype (self , s : Type , t : Type ) -> None :
291365 assert is_subtype (s , t ), f"{ s } not subtype of { t } "
292366
@@ -304,3 +378,53 @@ def assert_equivalent(self, s: Type, t: Type) -> None:
304378 def assert_unrelated (self , s : Type , t : Type ) -> None :
305379 self .assert_not_subtype (s , t )
306380 self .assert_not_subtype (t , s )
381+
382+
383+ class RestrictionSuite (Suite ):
384+ # Tests for type restrictions "A - B", i.e. ``T <: A and not T <: B``.
385+
386+ def setUp (self ) -> None :
387+ self .fx = TypeFixture ()
388+
389+ def assert_restriction (self , s : Type , t : Type , expected : Type ) -> None :
390+ actual = restrict_subtype_away (s , t )
391+ msg = f"restrict_subtype_away({ s } , { t } ) == {{}} ({{}} expected)"
392+ self .assertEqual (actual , expected , msg = msg .format (actual , expected ))
393+
394+ def test_literal (self ) -> None :
395+ str1 = self .fx .lit_str1
396+ str2 = self .fx .lit_str2
397+ str1_inst = self .fx .lit_str1_inst
398+ str2_inst = self .fx .lit_str2_inst
399+ str_type = self .fx .str_type
400+ uninhabited = self .fx .uninhabited
401+
402+ # other operand is the fallback type
403+ # "x" - str -> Never
404+ # str - "x" -> str
405+ # "x"? - str -> Never
406+ # str - "x"? -> Never
407+ self .assert_restriction (str1 , str_type , uninhabited )
408+ self .assert_restriction (str_type , str1 , str_type )
409+ self .assert_restriction (str1_inst , str_type , uninhabited )
410+ self .assert_restriction (str_type , str1_inst , uninhabited )
411+
412+ # other operand is the same literal
413+ # "x" - "x" -> Never
414+ # "x" - "x"? -> Never
415+ # "x"? - "x" -> Never
416+ # "x"? - "x"? -> Never
417+ self .assert_restriction (str1 , str1 , uninhabited )
418+ self .assert_restriction (str1 , str1_inst , uninhabited )
419+ self .assert_restriction (str1_inst , str1 , uninhabited )
420+ self .assert_restriction (str1_inst , str1_inst , uninhabited )
421+
422+ # other operand is a different literal
423+ # "x" - "y" -> "x"
424+ # "x" - "y"? -> Never
425+ # "x"? - "y" -> "x"?
426+ # "x"? - "y"? -> Never
427+ self .assert_restriction (str1 , str2 , str1 )
428+ self .assert_restriction (str1 , str2_inst , uninhabited )
429+ self .assert_restriction (str1_inst , str2 , str1_inst )
430+ self .assert_restriction (str1_inst , str2_inst , uninhabited )
0 commit comments