diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index ace6149bdc6b..78efc0536aa9 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -42,23 +42,27 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> bool: The logic in this function mostly copies the logic for visit_class_def() with a single (non-Generic) base. """ - name, call = self.analyze_newtype_declaration(s) - if name is None or call is None: + var_name, call = self.analyze_newtype_declaration(s) + if var_name is None or call is None: return False + name = var_name # OK, now we know this is a NewType. But the base type may be not ready yet, # add placeholder as we do for ClassDef. + if self.api.is_func_scope(): + name += '@' + str(s.line) fullname = self.api.qualified_name(name) + if (not call.analyzed or isinstance(call.analyzed, NewTypeExpr) and not call.analyzed.info): # Start from labeling this as a future class, as we do for normal ClassDefs. placeholder = PlaceholderNode(fullname, s, s.line, becomes_typeinfo=True) - self.api.add_symbol(name, placeholder, s, can_defer=False) + self.api.add_symbol(var_name, placeholder, s, can_defer=False) - old_type, should_defer = self.check_newtype_args(name, call, s) + old_type, should_defer = self.check_newtype_args(var_name, call, s) old_type = get_proper_type(old_type) if not call.analyzed: - call.analyzed = NewTypeExpr(name, old_type, line=call.line, column=call.column) + call.analyzed = NewTypeExpr(var_name, old_type, line=call.line, column=call.column) if old_type is None: if should_defer: # Base type is not ready. @@ -98,7 +102,9 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> bool: call.analyzed.info = newtype_class_info else: call.analyzed.info.bases = newtype_class_info.bases - self.api.add_symbol(name, call.analyzed.info, s) + self.api.add_symbol(var_name, call.analyzed.info, s) + if self.api.is_func_scope(): + self.api.add_symbol_skip_local(name, call.analyzed.info) newtype_class_info.line = s.line return True @@ -191,7 +197,7 @@ def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance) name=name) init_func = FuncDef('__init__', args, Block([]), typ=signature) init_func.info = info - init_func._fullname = self.api.qualified_name(name) + '.__init__' + init_func._fullname = info.fullname + '.__init__' info.names['__init__'] = SymbolTableNode(MDEF, init_func) return info diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 44262371253c..ced37c600f14 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -162,6 +162,10 @@ def qualified_name(self, n: str) -> str: def is_typeshed_stub_file(self) -> bool: raise NotImplementedError + @abstractmethod + def is_func_scope(self) -> bool: + raise NotImplementedError + def create_indirect_imported_name(file_node: MypyFile, module: str, diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 8a887685da05..5123d59519a9 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1896,6 +1896,28 @@ main:1: error: Module 'tdcrash' has no attribute 'nope' [out2] main:1: error: Module 'tdcrash' has no attribute 'nope' +[case testIncrementalNewTypeInMethod] +from ntcrash import nope +[file ntcrash.py] +from mypy_extensions import TypedDict +from typing import NewType, NamedTuple +class C: + def f(self) -> None: + X = NewType('X', int) + A = TypedDict('A', {'x': X, 'y': int}) + B = NamedTuple('B', [('x', X)]) + +def f() -> None: + X = NewType('X', int) + A = TypedDict('A', {'x': X, 'y': int}) + B = NamedTuple('B', [('x', X)]) + +[builtins fixtures/dict.pyi] +[out1] +main:1: error: Module 'ntcrash' has no attribute 'nope' +[out2] +main:1: error: Module 'ntcrash' has no attribute 'nope' + [case testIncrementalInnerClassAttrInMethod] import crash nonexisting @@ -2274,6 +2296,7 @@ tmp/c.py:1: error: Module 'd' has no attribute 'x' [out] [out2] mypy: can't read file 'tmp/nonexistent.py': No such file or directory +-- ' [case testSerializeAbstractPropertyIncremental] from abc import abstractmethod @@ -2484,7 +2507,7 @@ A = Dict[str, int] [out] -- Some crazy self-referential named tuples, types dicts, and aliases --- to be sure that everything can be _serialized_ (i.e. ForwardRef's are removed). +-- to be sure that everything can be _serialized_ (i.e. ForwardRefs are removed). -- For this reason errors are silenced (tests with # type: ignore have equivalents in other files) [case testForwardTypeAliasInBase1] diff --git a/test-data/unit/check-newtype.test b/test-data/unit/check-newtype.test index 2a49cc63738e..986a187d01b1 100644 --- a/test-data/unit/check-newtype.test +++ b/test-data/unit/check-newtype.test @@ -194,7 +194,7 @@ def func() -> None: A = NewType('A', str) B = NewType('B', str) - a = A(3) # E: Argument 1 to "A" has incompatible type "int"; expected "str" + a = A(3) # E: Argument 1 to "A@6" has incompatible type "int"; expected "str" a = A('xyz') b = B('xyz')