diff --git a/mypy/server/astmerge.py b/mypy/server/astmerge.py index edfdc076e7d7..32dd7157ccc8 100644 --- a/mypy/server/astmerge.py +++ b/mypy/server/astmerge.py @@ -328,7 +328,12 @@ def replace_statements(self, nodes: List[Statement]) -> List[Statement]: class TypeReplaceVisitor(SyntheticTypeVisitor[None]): - """Similar to NodeReplaceVisitor, but for type objects.""" + """Similar to NodeReplaceVisitor, but for type objects. + + Note: this visitor may sometimes visit unanalyzed types + such as 'UnboundType' and 'RawLiteralType' For example, see + NodeReplaceVisitor.process_base_func. + """ def __init__(self, replacements: Dict[SymbolNode, SymbolNode]) -> None: self.replacements = replacements @@ -393,7 +398,7 @@ def visit_typeddict_type(self, typ: TypedDictType) -> None: typ.fallback.accept(self) def visit_raw_literal_type(self, t: RawLiteralType) -> None: - assert False, "Unexpected RawLiteralType after semantic analysis phase" + pass def visit_literal_type(self, typ: LiteralType) -> None: typ.fallback.accept(self) diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index ad163eddfe9c..32076db8e802 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -38,6 +38,7 @@ 'contextlib', 'sys', 'mypy_extensions', + 'typing_extensions', 'enum', ) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index b3e70a12f70a..e054d6a35267 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -4890,3 +4890,17 @@ python_version=3.6 [out] [out2] tmp/a.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str") + +[case testLiteralIncrementalTurningIntoLiteral] +import mod +reveal_type(mod.a) +[file mod.py] +from typing_extensions import Literal +a = 1 +[file mod.py.2] +from typing_extensions import Literal +a: Literal[2] = 2 +[out] +main:2: error: Revealed type is 'builtins.int' +[out2] +main:2: error: Revealed type is 'Literal[2]' diff --git a/test-data/unit/deps-expressions.test b/test-data/unit/deps-expressions.test index 6a50b2f61425..eb87878ed6b3 100644 --- a/test-data/unit/deps-expressions.test +++ b/test-data/unit/deps-expressions.test @@ -448,3 +448,19 @@ def g() -> None: [out] -> m.g -> m.g + +[case testLiteralDepsExpr] +from typing_extensions import Literal + +Alias = Literal[1] + +a: Alias +b = a +def f(x: Alias) -> None: pass +def g() -> Literal[1]: + return b +[out] + -> m, m.f + -> m + -> m, m.g + -> m diff --git a/test-data/unit/diff.test b/test-data/unit/diff.test index ef3718522ce3..88067e28362a 100644 --- a/test-data/unit/diff.test +++ b/test-data/unit/diff.test @@ -1109,3 +1109,302 @@ plugins=/test-data/unit/plugins/dyn_class.py [out] __main__.Diff __main__.Diff.x + +[case testLiteralTriggersVar] +from typing_extensions import Literal + +x: Literal[1] = 1 +y = 1 +z: Literal[1] = 1 +same: Literal[1] = 1 +class C: + x_class: Literal[1] = 1 + y_class = 1 + z_class: Literal[1] = 1 + same_class: Literal[1] = 1 + def __init__(self) -> None: + self.x_instance: Literal[1] = 1 + self.y_instance = 1 + self.z_instance: Literal[1] = 1 + self.same_instance: Literal[1] = 1 + +[file next.py] +from typing_extensions import Literal + +x = 1 +y: Literal[1] = 1 +z: Literal[2] = 2 +same: Literal[1] = 1 +class C: + x_class = 1 + y_class: Literal[1] = 1 + z_class: Literal[2] = 2 + same_class: Literal[1] = 1 + def __init__(self) -> None: + self.x_instance = 1 + self.y_instance: Literal[1] = 1 + self.z_instance: Literal[2] = 2 + self.same_instance: Literal[1] = 1 +[out] +__main__.C.x_class +__main__.C.x_instance +__main__.C.y_class +__main__.C.y_instance +__main__.C.z_class +__main__.C.z_instance +__main__.x +__main__.y +__main__.z + +[case testLiteralTriggersFunctions] +from typing_extensions import Literal + +def function_1() -> int: pass +def function_2() -> Literal[1]: pass +def function_3() -> Literal[1]: pass + +def function_4(x: int) -> None: pass +def function_5(x: Literal[1]) -> None: pass +def function_6(x: Literal[1]) -> None: pass + +def function_same_1() -> Literal[1]: pass +def function_same_2(x: Literal[1]) -> None: pass + +class C: + def method_1(self) -> int: pass + def method_2(self) -> Literal[1]: pass + def method_3(self) -> Literal[1]: pass + + def method_4(self, x: int) -> None: pass + def method_5(self, x: Literal[1]) -> None: pass + def method_6(self, x: Literal[1]) -> None: pass + + def method_same_1(self) -> Literal[1]: pass + def method_same_2(self, x: Literal[1]) -> None: pass + + @classmethod + def classmethod_1(cls) -> int: pass + @classmethod + def classmethod_2(cls) -> Literal[1]: pass + @classmethod + def classmethod_3(cls) -> Literal[1]: pass + + @classmethod + def classmethod_4(cls, x: int) -> None: pass + @classmethod + def classmethod_5(cls, x: Literal[1]) -> None: pass + @classmethod + def classmethod_6(cls, x: Literal[1]) -> None: pass + + @classmethod + def classmethod_same_1(cls) -> Literal[1]: pass + @classmethod + def classmethod_same_2(cls, x: Literal[1]) -> None: pass + + @staticmethod + def staticmethod_1() -> int: pass + @staticmethod + def staticmethod_2() -> Literal[1]: pass + @staticmethod + def staticmethod_3() -> Literal[1]: pass + + @staticmethod + def staticmethod_4(x: int) -> None: pass + @staticmethod + def staticmethod_5(x: Literal[1]) -> None: pass + @staticmethod + def staticmethod_6(x: Literal[1]) -> None: pass + + @staticmethod + def staticmethod_same_1() -> Literal[1]: pass + @staticmethod + def staticmethod_same_2(x: Literal[1]) -> None: pass + +[file next.py] +from typing_extensions import Literal + +def function_1() -> Literal[1]: pass +def function_2() -> int: pass +def function_3() -> Literal[2]: pass + +def function_4(x: Literal[1]) -> None: pass +def function_5(x: int) -> None: pass +def function_6(x: Literal[2]) -> None: pass + +def function_same_1() -> Literal[1]: pass +def function_same_2(x: Literal[1]) -> None: pass + +class C: + def method_1(self) -> Literal[1]: pass + def method_2(self) -> int: pass + def method_3(self) -> Literal[2]: pass + + def method_4(self, x: Literal[1]) -> None: pass + def method_5(self, x: int) -> None: pass + def method_6(self, x: Literal[2]) -> None: pass + + def method_same_1(self) -> Literal[1]: pass + def method_same_2(self, x: Literal[1]) -> None: pass + + @classmethod + def classmethod_1(cls) -> Literal[1]: pass + @classmethod + def classmethod_2(cls) -> int: pass + @classmethod + def classmethod_3(cls) -> Literal[2]: pass + + @classmethod + def classmethod_4(cls, x: Literal[1]) -> None: pass + @classmethod + def classmethod_5(cls, x: int) -> None: pass + @classmethod + def classmethod_6(cls, x: Literal[2]) -> None: pass + + @classmethod + def classmethod_same_1(cls) -> Literal[1]: pass + @classmethod + def classmethod_same_2(cls, x: Literal[1]) -> None: pass + + @staticmethod + def staticmethod_1() -> Literal[1]: pass + @staticmethod + def staticmethod_2() -> int: pass + @staticmethod + def staticmethod_3() -> Literal[2]: pass + + @staticmethod + def staticmethod_4(x: Literal[1]) -> None: pass + @staticmethod + def staticmethod_5(x: int) -> None: pass + @staticmethod + def staticmethod_6(x: Literal[2]) -> None: pass + + @staticmethod + def staticmethod_same_1() -> Literal[1]: pass + @staticmethod + def staticmethod_same_2(x: Literal[1]) -> None: pass +[builtins fixtures/classmethod.pyi] +[out] +__main__.C.classmethod_1 +__main__.C.classmethod_2 +__main__.C.classmethod_3 +__main__.C.classmethod_4 +__main__.C.classmethod_5 +__main__.C.classmethod_6 +__main__.C.method_1 +__main__.C.method_2 +__main__.C.method_3 +__main__.C.method_4 +__main__.C.method_5 +__main__.C.method_6 +__main__.C.staticmethod_1 +__main__.C.staticmethod_2 +__main__.C.staticmethod_3 +__main__.C.staticmethod_4 +__main__.C.staticmethod_5 +__main__.C.staticmethod_6 +__main__.function_1 +__main__.function_2 +__main__.function_3 +__main__.function_4 +__main__.function_5 +__main__.function_6 + +[case testLiteralTriggersProperty] +from typing_extensions import Literal + +class C: + @property + def p1(self) -> Literal[1]: pass + + @property + def p2(self) -> int: pass + + @property + def same(self) -> Literal[1]: pass + +[file next.py] +from typing_extensions import Literal + +class C: + @property + def p1(self) -> int: pass + + @property + def p2(self) -> Literal[1]: pass + + @property + def same(self) -> Literal[1]: pass +[builtins fixtures/property.pyi] +[out] +__main__.C.p1 +__main__.C.p2 + +[case testLiteralsTriggersOverload] +from typing import overload +from typing_extensions import Literal + +@overload +def func(x: str) -> str: ... +@overload +def func(x: Literal[1]) -> int: ... +def func(x): + pass + +@overload +def func_same(x: str) -> str: ... +@overload +def func_same(x: Literal[1]) -> int: ... +def func_same(x): + pass + +class C: + @overload + def method(self, x: str) -> str: ... + @overload + def method(self, x: Literal[1]) -> int: ... + def method(self, x): + pass + + @overload + def method_same(self, x: str) -> str: ... + @overload + def method_same(self, x: Literal[1]) -> int: ... + def method_same(self, x): + pass + +[file next.py] +from typing import overload +from typing_extensions import Literal + +@overload +def func(x: str) -> str: ... +@overload +def func(x: Literal[2]) -> int: ... +def func(x): + pass + +@overload +def func_same(x: str) -> str: ... +@overload +def func_same(x: Literal[1]) -> int: ... +def func_same(x): + pass + +class C: + @overload + def method(self, x: str) -> str: ... + @overload + def method(self, x: Literal[2]) -> int: ... + def method(self, x): + pass + + @overload + def method_same(self, x: str) -> str: ... + @overload + def method_same(self, x: Literal[1]) -> int: ... + def method_same(self, x): + pass +[out] +__main__.C.method +__main__.func diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 8da12a200df9..68306f579273 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -8293,3 +8293,229 @@ B = func [out] == main:5: error: Invalid type "a.A" + +[case testLiteralFineGrainedVarConversion] +import mod +reveal_type(mod.x) +[file mod.py] +x = 1 +[file mod.py.2] +from typing_extensions import Literal +x: Literal[1] = 1 +[file mod.py.3] +from typing_extensions import Literal +x: Literal[1] = 2 +[out] +main:2: error: Revealed type is 'builtins.int' +== +main:2: error: Revealed type is 'Literal[1]' +== +mod.py:2: error: Incompatible types in assignment (expression has type "Literal[2]", variable has type "Literal[1]") +main:2: error: Revealed type is 'Literal[1]' + +[case testLiteralFineGrainedFunctionConversion] +from mod import foo +foo(3) +[file mod.py] +def foo(x: int) -> None: pass +[file mod.py.2] +from typing_extensions import Literal +def foo(x: Literal[3]) -> None: pass +[file mod.py.3] +from typing_extensions import Literal +def foo(x: Literal[4]) -> None: pass +[out] +== +== +main:2: error: Argument 1 to "foo" has incompatible type "Literal[3]"; expected "Literal[4]" + +[case testLiteralFineGrainedAlias] +from mod import Alias +a: Alias = 1 +[file mod.py] +Alias = int +[file mod.py.2] +from typing_extensions import Literal +Alias = Literal[1] +[file mod.py.3] +from typing_extensions import Literal +Alias = Literal[2] +[out] +== +== +main:2: error: Incompatible types in assignment (expression has type "Literal[1]", variable has type "Literal[2]") + +[case testLiteralFineGrainedOverload] +from mod import foo +reveal_type(foo(4)) +[file mod.py] +from typing import overload +from typing_extensions import Literal +@overload +def foo(x: int) -> str: ... +@overload +def foo(x: Literal['bar']) -> int: ... +def foo(x): pass +[file mod.py.2] +from typing import overload +from typing_extensions import Literal +@overload +def foo(x: Literal[4]) -> Literal['foo']: ... +@overload +def foo(x: int) -> str: ... +@overload +def foo(x: Literal['bar']) -> int: ... +def foo(x): pass +[out] +main:2: error: Revealed type is 'builtins.str' +== +main:2: error: Revealed type is 'Literal['foo']' + +[case testLiteralFineGrainedChainedDefinitions] +from mod1 import foo +from typing_extensions import Literal +def expect_3(x: Literal[3]) -> None: pass +expect_3(foo) +[file mod1.py] +from mod2 import bar +foo = bar +[file mod2.py] +from mod3 import qux as bar +[file mod3.py] +from typing_extensions import Literal +qux: Literal[3] +[file mod3.py.2] +from typing_extensions import Literal +qux: Literal[4] +[out] +== +main:4: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expected "Literal[3]" + +[case testLiteralFineGrainedChainedAliases] +from mod1 import Alias1 +from typing_extensions import Literal +x: Alias1 +def expect_3(x: Literal[3]) -> None: pass +expect_3(x) +[file mod1.py] +from mod2 import Alias2 +Alias1 = Alias2 +[file mod2.py] +from mod3 import Alias3 +Alias2 = Alias3 +[file mod3.py] +from typing_extensions import Literal +Alias3 = Literal[3] +[file mod3.py.2] +from typing_extensions import Literal +Alias3 = Literal[4] +[out] +== +main:5: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expected "Literal[3]" + +[case testLiteralFineGrainedChainedFunctionDefinitions] +from mod1 import func1 +from typing_extensions import Literal +def expect_3(x: Literal[3]) -> None: pass +expect_3(func1()) +[file mod1.py] +from mod2 import func2 as func1 +[file mod2.py] +from mod3 import func3 +func2 = func3 +[file mod3.py] +from typing_extensions import Literal +def func3() -> Literal[3]: pass +[file mod3.py.2] +from typing_extensions import Literal +def func3() -> Literal[4]: pass +[out] +== +main:4: error: Argument 1 to "expect_3" has incompatible type "Literal[4]"; expected "Literal[3]" + +[case testLiteralFineGrainedChainedTypeVarInference] +from mod1 import foo +reveal_type(foo) +[file mod1.py] +from typing import TypeVar +from mod2 import bar +T = TypeVar('T', bound=int) +def func(x: T) -> T: return x +foo = func(bar) +[file mod2.py] +bar = 3 +[file mod2.py.2] +from typing_extensions import Literal +bar: Literal[3] = 3 +[out] +main:2: error: Revealed type is 'builtins.int*' +== +main:2: error: Revealed type is 'Literal[3]' + +[case testLiteralFineGrainedStringConversionPython3] +from mod1 import foo +reveal_type(foo) +[file mod1.py] +from mod2 import bar +foo = bar() +[file mod2.py] +from typing_extensions import Literal +def bar() -> Literal["foo"]: pass +[file mod2.py.2] +from typing_extensions import Literal +def bar() -> Literal[u"foo"]: pass +[file mod2.py.3] +from typing_extensions import Literal +def bar() -> Literal[b"foo"]: pass +[out] +main:2: error: Revealed type is 'Literal['foo']' +== +main:2: error: Revealed type is 'Literal['foo']' +== +main:2: error: Revealed type is 'Literal[b'foo']' + +[case testLiteralFineGrainedStringConversionPython2] +# flags: --python-version 2.7 +from mod1 import foo +reveal_type(foo) +[file mod1.py] +from mod2 import bar +foo = bar() +[file mod2.py] +from typing_extensions import Literal +def bar(): + # type: () -> Literal["foo"] + pass +[file mod2.py.2] +from typing_extensions import Literal +def bar(): + # type: () -> Literal[b"foo"] + pass +[file mod2.py.3] +from __future__ import unicode_literals +from typing_extensions import Literal +def bar(): + # type: () -> Literal["foo"] + pass +[file mod2.py.4] +from __future__ import unicode_literals +from typing_extensions import Literal +def bar(): + # type: () -> Literal[b"foo"] + pass +[file mod2.py.5] +from typing_extensions import Literal +def bar(): + # type: () -> Literal[u"foo"] + pass +[out] +main:3: error: Revealed type is 'Literal['foo']' +== +main:3: error: Revealed type is 'Literal['foo']' +== +main:3: error: Revealed type is 'Literal[u'foo']' +== +main:3: error: Revealed type is 'Literal['foo']' +== +main:3: error: Revealed type is 'Literal[u'foo']' + diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test index 281006557083..748172efeac3 100644 --- a/test-data/unit/merge.test +++ b/test-data/unit/merge.test @@ -1451,3 +1451,51 @@ TypeInfo<0>( X<3> (builtins.int<4>) Y<6> (builtins.int<4>)) MetaclassType(enum.EnumMeta<5>)) + +[case testLiteralMerge] +import target +[file target.py] +from typing_extensions import Literal +def foo(x: Literal[3]) -> Literal['a']: pass +bar: Literal[4] = 4 +[file target.py.next] +from typing_extensions import Literal +def foo(x: Literal['3']) -> Literal['b']: pass +bar: Literal[5] = 5 +[out] +MypyFile:1<0>( + tmp/main + Import:1(target)) +MypyFile:1<1>( + tmp/target.py + ImportFrom:1(typing_extensions, [Literal]) + FuncDef:2<2>( + foo + Args( + Var(x)) + def (x: Literal[3]) -> Literal['a'] + Block:2<3>( + PassStmt:2<4>())) + AssignmentStmt:3<5>( + NameExpr(bar [target.bar<6>]) + IntExpr(4) + Literal[4])) +==> +MypyFile:1<0>( + tmp/main + Import:1(target)) +MypyFile:1<1>( + tmp/target.py + ImportFrom:1(typing_extensions, [Literal]) + FuncDef:2<2>( + foo + Args( + Var(x)) + def (x: Literal['3']) -> Literal['b'] + Block:2<7>( + PassStmt:2<8>())) + AssignmentStmt:3<9>( + NameExpr(bar [target.bar<6>]) + IntExpr(5) + Literal[5])) +