From a5bd90c88ee9760c6ebe1aab56f41953d163191d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Aug 2018 20:36:20 +0100 Subject: [PATCH] Move analysis of function signature to the first phase --- mypy/semanal.py | 35 +++++++------ test-data/unit/check-overloading.test | 71 +++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 15 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 678397f59759..c452daca448e 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -416,6 +416,26 @@ def _visit_func_def(self, defn: FuncDef) -> None: if not self.set_original_def(symbol.node, defn): # Report error. self.check_no_global(defn.name(), defn, True) + + # Analyze function signature and initializers in the first phase + # (at least this mirrors what happens at runtime). + with self.tvar_scope_frame(self.tvar_scope.method_frame()): + if defn.type: + self.check_classvar_in_signature(defn.type) + assert isinstance(defn.type, CallableType) + # Signature must be analyzed in the surrounding scope so that + # class-level imported names and type variables are in scope. + analyzer = self.type_analyzer() + defn.type = analyzer.visit_callable_type(defn.type, nested=False) + self.add_type_alias_deps(analyzer.aliases_used) + self.check_function_signature(defn) + if isinstance(defn, FuncDef): + assert isinstance(defn.type, CallableType) + defn.type = set_callable_name(defn.type, defn) + for arg in defn.arguments: + if arg.initializer: + arg.initializer.accept(self) + if phase_info == FUNCTION_FIRST_PHASE_POSTPONE_SECOND: # Postpone this function (for the second phase). self.postponed_functions_stack[-1].append(defn) @@ -646,21 +666,6 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) - def analyze_function(self, defn: FuncItem) -> None: is_method = self.is_class_scope() with self.tvar_scope_frame(self.tvar_scope.method_frame()): - if defn.type: - self.check_classvar_in_signature(defn.type) - assert isinstance(defn.type, CallableType) - # Signature must be analyzed in the surrounding scope so that - # class-level imported names and type variables are in scope. - analyzer = self.type_analyzer() - defn.type = analyzer.visit_callable_type(defn.type, nested=False) - self.add_type_alias_deps(analyzer.aliases_used) - self.check_function_signature(defn) - if isinstance(defn, FuncDef): - assert isinstance(defn.type, CallableType) - defn.type = set_callable_name(defn.type, defn) - for arg in defn.arguments: - if arg.initializer: - arg.initializer.accept(self) # Bind the type variables again to visit the body. if defn.type: a = self.type_analyzer() diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 160a1a84732c..ef26f7f91484 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -4186,3 +4186,74 @@ def g(x: str) -> int: ... [builtins fixtures/list.pyi] [typing fixtures/typing-full.pyi] [out] + +[case testNestedOverloadsNoCrash] +from typing import overload + +def f() -> None: + @overload + def g(x: str) -> str: ... + @overload + def g(x: int) -> int: ... + def g(x): + pass + g(str()) +[out] + +[case testNestedOverloadsTypeVar] +from typing import overload, TypeVar + +T = TypeVar('T') + +def f() -> None: + @overload + def g(x: str) -> str: ... + @overload + def g(x: T, y: int) -> T: ... + def g(x): + pass + + g(str(), str()) # E: No overload variant of "g" matches argument types "str", "str" \ + # N: Possible overload variant: \ + # N: def [T] g(x: T, y: int) -> T \ + # N: <1 more non-matching overload not shown> + reveal_type(g(str(), int())) # E: Revealed type is 'builtins.str*' +[out] + +[case testNestedOverloadsTypeVarOverlap] +from typing import overload, TypeVar + +T = TypeVar('T') + +def f() -> None: + @overload + def g(x: str) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types + @overload + def g(x: T) -> T: ... + def g(x): + pass +[out] + +[case testNestedOverloadsMutuallyRecursive] +from typing import overload, TypeVar, Dict, Any + +class C: ... +T = TypeVar('T') + +def f() -> None: + @overload + def g() -> None: ... + @overload + def g(x: T) -> Dict[int, T]: ... + def g(*args, **kwargs) -> Any: + reveal_type(h(C())) # E: Revealed type is 'builtins.dict[builtins.str, __main__.C*]' + + @overload + def h() -> None: ... + @overload + def h(x: T) -> Dict[str, T]: ... + def h(*args, **kwargs) -> Any: + reveal_type(g(C())) # E: Revealed type is 'builtins.dict[builtins.int, __main__.C*]' + +[builtins fixtures/dict.pyi] +[out]