diff --git a/mypy/checker.py b/mypy/checker.py index 80402e71dce6..08f8e022db34 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -8577,6 +8577,9 @@ def is_func_scope(self) -> bool: # message types are ignored. return False + def is_nested_within_func_scope(self) -> bool: + return self._chk.scope.top_level_function() is not None + @property def type(self) -> TypeInfo | None: return self._chk.scope.current_class() diff --git a/mypy/semanal.py b/mypy/semanal.py index da58c9586966..230a149282ca 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2112,7 +2112,7 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> bool: if info is None: self.mark_incomplete(defn.name, defn) else: - self.prepare_class_def(defn, info, custom_names=True) + self.prepare_class_def(defn, info) for decorator in defn.decorators: decorator.accept(self) if defn.info: @@ -2136,13 +2136,13 @@ def analyze_namedtuple_classdef( info: TypeInfo | None = defn.info else: is_named_tuple, info = self.named_tuple_analyzer.analyze_namedtuple_classdef( - defn, self.is_stub_file, self.is_func_scope() + defn, self.is_stub_file ) if is_named_tuple: if info is None: self.mark_incomplete(defn.name, defn) else: - self.prepare_class_def(defn, info, custom_names=True) + self.prepare_class_def(defn, info) self.setup_type_vars(defn, tvar_defs) self.setup_alias_type_vars(defn) with self.scope.class_scope(defn.info): @@ -2510,51 +2510,24 @@ def get_and_bind_all_tvars(self, type_exprs: list[Expression]) -> list[TypeVarLi tvar_defs.append(tvar_def) return tvar_defs - def prepare_class_def( - self, defn: ClassDef, info: TypeInfo | None = None, custom_names: bool = False - ) -> None: + def prepare_class_def(self, defn: ClassDef, info: TypeInfo | None = None) -> None: """Prepare for the analysis of a class definition. Create an empty TypeInfo and store it in a symbol table, or if the 'info' argument is provided, store it instead (used for magic type definitions). """ if not defn.info: - defn.fullname = self.qualified_name(defn.name) - # TODO: Nested classes + if not self.is_nested_within_func_scope(): + defn.fullname = self.qualified_name(defn.name) + else: + defn.fullname = f"{self.cur_mod_id}.{defn.name}@{defn.line}" info = info or self.make_empty_type_info(defn) defn.info = info info.defn = defn - if not custom_names: - # Some special classes (in particular NamedTuples) use custom fullname logic. - # Don't override it here (also see comment below, this needs cleanup). - if not self.is_func_scope(): - info._fullname = self.qualified_name(defn.name) - else: - info._fullname = info.name - local_name = defn.name - if "@" in local_name: - local_name = local_name.split("@")[0] - self.add_symbol(local_name, defn.info, defn) + self.add_symbol(defn.name, defn.info, defn) if self.is_nested_within_func_scope(): - # We need to preserve local classes, let's store them - # in globals under mangled unique names - # - # TODO: Putting local classes into globals breaks assumptions in fine-grained - # incremental mode and we should avoid it. In general, this logic is too - # ad-hoc and needs to be removed/refactored. - if "@" not in defn.info._fullname: - global_name = defn.info.name + "@" + str(defn.line) - defn.info._fullname = self.cur_mod_id + "." + global_name - else: - # Preserve name from previous fine-grained incremental run. - global_name = defn.info.name - defn.fullname = defn.info._fullname - if defn.info.is_named_tuple or defn.info.typeddict_type: - # Named tuples and Typed dicts nested within a class are stored - # in the class symbol table. - self.add_symbol_skip_local(global_name, defn.info) - else: - self.globals[global_name] = SymbolTableNode(GDEF, defn.info) + global_name = f"{defn.name}@{defn.line}" + self.globals[global_name] = SymbolTableNode(GDEF, defn.info) def make_empty_type_info(self, defn: ClassDef) -> TypeInfo: if ( @@ -3592,7 +3565,7 @@ def analyze_enum_assign(self, s: AssignmentStmt) -> bool: # This is an analyzed enum definition. # It is valid iff it can be stored correctly, failures were already reported. return self._is_single_name_assignment(s) - return self.enum_call_analyzer.process_enum_call(s, self.is_func_scope()) + return self.enum_call_analyzer.process_enum_call(s) def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: """Check if s defines a namedtuple.""" @@ -3616,7 +3589,7 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: namespace = self.qualified_name(name) with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): internal_name, info, tvar_defs = self.named_tuple_analyzer.check_namedtuple( - s.rvalue, name, self.is_func_scope() + s.rvalue, name ) if internal_name is None: return False @@ -3653,7 +3626,7 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool: namespace = self.qualified_name(name) with self.tvar_scope_frame(self.tvar_scope.class_frame(namespace)): is_typed_dict, info, tvar_defs = self.typed_dict_analyzer.check_typeddict( - s.rvalue, name, self.is_func_scope() + s.rvalue, name ) if not is_typed_dict: return False @@ -5159,15 +5132,19 @@ def process_typevartuple_declaration(self, s: AssignmentStmt) -> bool: return True def basic_new_typeinfo(self, name: str, basetype_or_fallback: Instance, line: int) -> TypeInfo: - if self.is_func_scope() and not self.type and "@" not in name: - name += "@" + str(line) class_def = ClassDef(name, Block([])) - if self.is_func_scope() and not self.type: - # Full names of generated classes should always be prefixed with the module names - # even if they are nested in a function, since these classes will be (de-)serialized. - # (Note that the caller should append @line to the name to avoid collisions.) - # TODO: clean this up, see #6422. - class_def.fullname = self.cur_mod_id + "." + self.qualified_name(name) + # Ground rules for classes nested in functions: + # * Use is_nested_within_func_scope(), not is_func_scope(), to determine whether + # to use any special logic, because nothing inside top-level functions is serialized. + # * ClassDef.name is not mangled (i.e. @line suffix is not appended). + # * ClassDef.fullname, and thus TypeInfo.fullname are always pkg.mod.Name@line, any + # "intermediate" classes are not included in the fullname. + # * The caller is responsible for storing the generated TypeInfo twice: once as usual + # with add_symbol(), and once using add_global_symbol() using the mangled name. + # The second one is needed to properly serialize any classes nested in functions. + # TODO: make sure the daemon works well with these rules. + if self.is_nested_within_func_scope(): + class_def.fullname = f"{self.cur_mod_id}.{name}@{line}" else: class_def.fullname = self.qualified_name(name) @@ -7028,27 +7005,18 @@ def add_symbol( name, symbol, context, can_defer, escape_comprehensions, no_progress, type_param ) - def add_symbol_skip_local(self, name: str, node: SymbolNode) -> None: - """Same as above, but skipping the local namespace. + def add_global_symbol(self, name: str, node: SymbolNode) -> None: + """Add symbol to a global namespace. This doesn't check for previous definition and is only used - for serialization of method-level classes. + for serialization of classes nested in functions/methods. Classes defined within methods can be exposed through an attribute type, but method-level symbol tables aren't serialized. This method can be used to add such classes to an enclosing, serialized symbol table. """ - # TODO: currently this is only used by named tuples and typed dicts. - # Use this method also by normal classes, see issue #6422. - if self.type is not None: - names = self.type.names - kind = MDEF - else: - names = self.globals - kind = GDEF - symbol = SymbolTableNode(kind, node) - names[name] = symbol + self.globals[name] = SymbolTableNode(GDEF, node) def add_symbol_table_node( self, @@ -7714,9 +7682,7 @@ def expr_to_analyzed_type( # backwards compatibility, but new features like generic named tuples # and recursive named tuples will be not supported. expr.accept(self) - internal_name, info, tvar_defs = self.named_tuple_analyzer.check_namedtuple( - expr, None, self.is_func_scope() - ) + internal_name, info, tvar_defs = self.named_tuple_analyzer.check_namedtuple(expr, None) if tvar_defs: self.fail("Generic named tuples are not supported for legacy class syntax", expr) self.note("Use either Python 3 class syntax, or the assignment syntax", expr) diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index b1e267b4c781..96d9cd6ac337 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing import Final, cast +from typing import Final from mypy.nodes import ( ARG_NAMED, @@ -60,7 +60,7 @@ def __init__(self, options: Options, api: SemanticAnalyzerInterface) -> None: self.options = options self.api = api - def process_enum_call(self, s: AssignmentStmt, is_func_scope: bool) -> bool: + def process_enum_call(self, s: AssignmentStmt) -> bool: """Check if s defines an Enum; if yes, store the definition in symbol table. Return True if this looks like an Enum definition (but maybe with errors), @@ -70,7 +70,7 @@ def process_enum_call(self, s: AssignmentStmt, is_func_scope: bool) -> bool: return False lvalue = s.lvalues[0] name = lvalue.name - enum_call = self.check_enum_call(s.rvalue, name, is_func_scope) + enum_call = self.check_enum_call(s.rvalue, name) if enum_call is None: return False if isinstance(lvalue, MemberExpr): @@ -80,9 +80,7 @@ def process_enum_call(self, s: AssignmentStmt, is_func_scope: bool) -> bool: self.api.add_symbol(name, enum_call, s) return True - def check_enum_call( - self, node: Expression, var_name: str, is_func_scope: bool - ) -> TypeInfo | None: + def check_enum_call(self, node: Expression, var_name: str) -> TypeInfo | None: """Check if a call defines an Enum. Example: @@ -110,23 +108,15 @@ class A(enum.Enum): ) if not ok: # Error. Construct dummy return value. - name = var_name - if is_func_scope: - name += "@" + str(call.line) - info = self.build_enum_call_typeinfo(name, [], fullname, node.line) + info = self.build_enum_call_typeinfo(var_name, [], fullname, node.line) else: if new_class_name != var_name: msg = f'String argument 1 "{new_class_name}" to {fullname}(...) does not match variable name "{var_name}"' self.fail(msg, call) - - name = cast(StrExpr, call.args[0]).value - if name != var_name or is_func_scope: - # Give it a unique name derived from the line number. - name += "@" + str(call.line) - info = self.build_enum_call_typeinfo(name, items, fullname, call.line) + info = self.build_enum_call_typeinfo(var_name, items, fullname, call.line) # Store generated TypeInfo under both names, see semanal_namedtuple for more details. - if name != var_name or is_func_scope: - self.api.add_symbol_skip_local(name, info) + if self.api.is_nested_within_func_scope(): + self.api.add_global_symbol(f"{var_name}@{node.line}", info) call.analyzed = EnumCallExpr(info, items, values) call.analyzed.set_line(call) info.line = node.line diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 08fc3e2bb772..0a44c27dc6fc 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -105,7 +105,7 @@ def __init__( self.msg = msg def analyze_namedtuple_classdef( - self, defn: ClassDef, is_stub_file: bool, is_func_scope: bool + self, defn: ClassDef, is_stub_file: bool ) -> tuple[bool, TypeInfo | None]: """Analyze if given class definition can be a named tuple definition. @@ -122,8 +122,6 @@ def analyze_namedtuple_classdef( # This is a valid named tuple, but some types are incomplete. return True, None items, types, default_items, statements = result - if is_func_scope and "@" not in defn.name: - defn.name += "@" + str(defn.line) existing_info = None if isinstance(defn.analyzed, NamedTupleExpr): existing_info = defn.analyzed.info @@ -221,7 +219,7 @@ def check_namedtuple_classdef( return items, types, default_items, statements def check_namedtuple( - self, node: Expression, var_name: str | None, is_func_scope: bool + self, node: Expression, var_name: str | None ) -> tuple[str | None, TypeInfo | None, list[TypeVarLikeType]]: """Check if a call defines a namedtuple. @@ -256,15 +254,14 @@ def check_namedtuple( # Error. Construct dummy return value. if var_name: name = var_name - if is_func_scope: - name += "@" + str(call.line) else: name = var_name = "namedtuple@" + str(call.line) info = self.build_namedtuple_typeinfo(name, [], [], {}, node.line, None) self.store_namedtuple_info(info, var_name, call, is_typed) - if name != var_name or is_func_scope: - # NOTE: we skip local namespaces since they are not serialized. - self.api.add_symbol_skip_local(name, info) + if self.api.is_nested_within_func_scope(): + # NOTE: we always serialize in global namespace for convenience, + # because local namespaces are never serialized. + self.api.add_global_symbol(f"{name}@{call.line}", info) return var_name, info, [] if not ok: # This is a valid named tuple but some types are not ready. @@ -280,16 +277,14 @@ def check_namedtuple( else: name = typename - if var_name is None or is_func_scope: - # There are two special cases where need to give it a unique name derived - # from the line number: + if var_name is None: + # There are two special cases where need to give it a unique name: # * This is a base class expression, since it often matches the class name: # class NT(NamedTuple('NT', [...])): # ... - # * This is a local (function or method level) named tuple, since - # two methods of a class can define a named tuple with the same name, - # and they will be stored in the same namespace (see below). - name += "@" + str(call.line) + # * This is a local (function or method level) named tuple, this case is + # handled by basic_new_typeinfo(). + name = "namedtuple@" + name if defaults: default_items = { arg_name: default for arg_name, default in zip(items[-len(defaults) :], defaults) @@ -312,21 +307,18 @@ def check_namedtuple( else: call.analyzed = NamedTupleExpr(info, is_typed=is_typed) call.analyzed.set_line(call) - # There are three cases where we need to store the generated TypeInfo + # There are two cases where we need to store the generated TypeInfo # second time (for the purpose of serialization): - # * If there is a name mismatch like One = NamedTuple('Other', [...]) - # we also store the info under name 'Other@lineno', this is needed - # because classes are (de)serialized using their actual fullname, not - # the name of l.h.s. # * If this is a method level named tuple. It can leak from the method # via assignment to self attribute and therefore needs to be serialized # (local namespaces are not serialized). # * If it is a base class expression. It was not stored above, since # there is no var_name (but it still needs to be serialized # since it is in MRO of some class). - if name != var_name or is_func_scope: - # NOTE: we skip local namespaces since they are not serialized. - self.api.add_symbol_skip_local(name, info) + if var_name is None or self.api.is_nested_within_func_scope(): + if self.api.is_nested_within_func_scope(): + name = f"{name}@{call.line}" + self.api.add_global_symbol(name, info) return typename, info, tvar_defs def store_namedtuple_info( diff --git a/mypy/semanal_newtype.py b/mypy/semanal_newtype.py index e1d62c4410c9..23d96b13a64a 100644 --- a/mypy/semanal_newtype.py +++ b/mypy/semanal_newtype.py @@ -67,9 +67,6 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> bool: 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: @@ -134,8 +131,8 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> bool: else: call.analyzed.info.bases = newtype_class_info.bases 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) + if self.api.is_nested_within_func_scope(): + self.api.add_global_symbol(f"{var_name}@{s.line}", call.analyzed.info) newtype_class_info.line = s.line return True diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index a85d4ed00b5e..46af5d4f5314 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -137,6 +137,10 @@ def is_stub_file(self) -> bool: def is_func_scope(self) -> bool: raise NotImplementedError + @abstractmethod + def is_nested_within_func_scope(self) -> bool: + raise NotImplementedError + @property @abstractmethod def type(self) -> TypeInfo | None: @@ -230,13 +234,8 @@ def add_symbol( raise NotImplementedError @abstractmethod - def add_symbol_skip_local(self, name: str, node: SymbolNode) -> None: - """Add symbol to the current symbol table, skipping locals. - - This is used to store symbol nodes in a symbol table that - is going to be serialized (local namespaces are not serialized). - See implementation docstring for more details. - """ + def add_global_symbol(self, name: str, node: SymbolNode) -> None: + """Add symbol directly to the global symbol table.""" raise NotImplementedError @abstractmethod diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 3655e4c89dd4..55885d458357 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -113,8 +113,6 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N ) if field_types is None: return True, None # Defer - if self.api.is_func_scope() and "@" not in defn.name: - defn.name += "@" + str(defn.line) info = self.build_typeddict_typeinfo( defn.name, field_types, required_keys, readonly_keys, defn.line, existing_info ) @@ -128,7 +126,7 @@ def analyze_typeddict_classdef(self, defn: ClassDef) -> tuple[bool, TypeInfo | N typeddict_bases: list[Expression] = [] typeddict_bases_set = set() for expr in defn.base_type_exprs: - ok, maybe_type_info, _ = self.check_typeddict(expr, None, False) + ok, maybe_type_info, _ = self.check_typeddict(expr, None) if ok and maybe_type_info is not None: # expr is a CallExpr info = maybe_type_info @@ -406,7 +404,7 @@ def extract_meta_info( return typ, is_required, readonly def check_typeddict( - self, node: Expression, var_name: str | None, is_func_scope: bool + self, node: Expression, var_name: str | None ) -> tuple[bool, TypeInfo | None, list[TypeVarLikeType]]: """Check if a call defines a TypedDict. @@ -438,8 +436,6 @@ def check_typeddict( # Error. Construct dummy return value. if var_name: name = var_name - if is_func_scope: - name += "@" + str(call.line) else: name = var_name = "TypedDict@" + str(call.line) info = self.build_typeddict_typeinfo(name, {}, set(), set(), call.line, None) @@ -452,9 +448,6 @@ def check_typeddict( node, code=codes.NAME_MATCH, ) - if name != var_name or is_func_scope: - # Give it a unique name derived from the line number. - name += "@" + str(call.line) required_keys = { field for (field, t) in zip(items, types) @@ -481,6 +474,11 @@ def check_typeddict( existing_info = None if isinstance(node.analyzed, TypedDictExpr): existing_info = node.analyzed.info + + if var_name is None: + # Give it unique name in case a TypedDict() call is used as a base class. + name = "TypedDict@" + name + info = self.build_typeddict_typeinfo( name, dict(zip(items, types)), @@ -491,10 +489,12 @@ def check_typeddict( ) info.line = node.line # Store generated TypeInfo under both names, see semanal_namedtuple for more details. - if name != var_name or is_func_scope: - self.api.add_symbol_skip_local(name, info) if var_name: - self.api.add_symbol(var_name, info, node) + self.api.add_symbol(name, info, node) + if var_name is None or self.api.is_nested_within_func_scope(): + if self.api.is_nested_within_func_scope(): + name = f"{name}@{node.line}" + self.api.add_global_symbol(name, info) call.analyzed = TypedDictExpr(info) call.analyzed.set_line(call) return True, info, tvar_defs diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 5a66eff2bd3b..9d8baebe6b8e 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -9412,3 +9412,12 @@ from typ import NT def f() -> NT: return NT(x='') [builtins fixtures/tuple.pyi] + +[case testClassNestedInFunctionNotLeaking] +from a import X # E: Module "a" has no attribute "X" +reveal_type(X) # N: Revealed type is "Any" +[file a.py] +def f() -> None: + class X: + ... + x # E: Name "x" is not defined diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 55a20e2b3fa2..20cf3b5aebfb 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -604,8 +604,9 @@ V = Enum('U', **{'a': 1}) # E: Unexpected arguments to Enum() W = Enum('W', 'a b') W.c # E: "type[W]" has no attribute "c" X = Enum('Something', 'a b') # E: String argument 1 "Something" to enum.Enum(...) does not match variable name "X" -reveal_type(X.a) # N: Revealed type is "Literal[__main__.Something@23.a]?" -X.asdf # E: "type[Something@23]" has no attribute "asdf" +reveal_type(X.a) # N: Revealed type is "Literal[__main__.X.a]?" +X.asdf # E: "type[X]" has no attribute "asdf" + [builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] @@ -633,7 +634,7 @@ a = A() reveal_type(a.x) [builtins fixtures/enum.pyi] [out] -main:7: note: Revealed type is "__main__.A.E@4" +main:7: note: Revealed type is "__main__.E@4" [case testFunctionalEnumInClassBody] from enum import Enum diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index db15b7341910..c2d939a3dc9f 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5188,7 +5188,7 @@ class C: [builtins fixtures/tuple.pyi] [out] [out2] -tmp/a.py:3: note: Revealed type is "tuple[builtins.int, fallback=b.C.Hidden@5]" +tmp/a.py:3: note: Revealed type is "tuple[builtins.int, fallback=b.Hidden@5]" [case testIncrementalNodeCreatedFromGetattr] import a @@ -6892,7 +6892,7 @@ class C: [typing fixtures/typing-typeddict.pyi] [out] [out2] -tmp/a.py:3: note: Revealed type is "TypedDict('b.C.Hidden@5', {'x': builtins.int})" +tmp/a.py:3: note: Revealed type is "TypedDict('b.Hidden@5', {'x': builtins.int})" [case testNoIncrementalCrashOnInvalidEnumMethod] import a @@ -6916,7 +6916,7 @@ class TheClass: [builtins fixtures/tuple.pyi] [out] [out2] -tmp/a.py:3: note: Revealed type is "def (value: builtins.object) -> lib.TheClass.pyenum@6" +tmp/a.py:3: note: Revealed type is "def (value: builtins.object) -> lib.pyenum@6" -- Note: do not use _no_parallel unless really needed! [case testIncrementalFunctoolsPartial_no_parallel] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 4148d04014a8..b0b01ad3a593 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -936,8 +936,8 @@ class C: self.o: Out c = C() -reveal_type(c.o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6], __main__.Other@7, fallback=__main__.C.Out@5]" -reveal_type(c.o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other@7, fallback=__main__.C.In@6]" +reveal_type(c.o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other@7, fallback=__main__.In@6], __main__.Other@7, fallback=__main__.Out@5]" +reveal_type(c.o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other@7, fallback=__main__.In@6]" [builtins fixtures/tuple.pyi] [case testNewAnalyzerNamedTupleClassNestedMethod] @@ -956,9 +956,9 @@ class C: self.o: Out c = C() -reveal_type(c.o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9], __main__.Other@12, fallback=__main__.C.Out@5]" -reveal_type(c.o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]" -reveal_type(c.o.method()) # N: Revealed type is "tuple[builtins.str, __main__.Other@12, fallback=__main__.C.In@9]" +reveal_type(c.o) # N: Revealed type is "tuple[tuple[builtins.str, __main__.Other@12, fallback=__main__.In@9], __main__.Other@12, fallback=__main__.Out@5]" +reveal_type(c.o.x) # N: Revealed type is "tuple[builtins.str, __main__.Other@12, fallback=__main__.In@9]" +reveal_type(c.o.method()) # N: Revealed type is "tuple[builtins.str, __main__.Other@12, fallback=__main__.In@9]" [builtins fixtures/tuple.pyi] [case testNewAnalyzerNamedTupleClassForwardMethod] diff --git a/test-data/unit/check-newtype.test b/test-data/unit/check-newtype.test index a0789596a479..95042d1393ab 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@6" has incompatible type "int"; expected "str" + a = A(3) # E: Argument 1 to "A" has incompatible type "int"; expected "str" a = A('xyz') b = B('xyz') @@ -206,7 +206,6 @@ class MyClass: b = A(3) c = MyClass.C(3.5) -[out] [case testNewTypeInMultipleFiles] import a diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test index 7a257bc017c3..7bc7f78bd961 100644 --- a/test-data/unit/check-serialize.test +++ b/test-data/unit/check-serialize.test @@ -729,13 +729,13 @@ class C: self.c = A [builtins fixtures/tuple.pyi] [out1] -main:2: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]" -main:3: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]" -main:4: note: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=ntcrash.C.A@4]" +main:2: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.A@4]" +main:3: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.A@4]" +main:4: note: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=ntcrash.A@4]" [out2] -main:2: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]" -main:3: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.C.A@4]" -main:4: note: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=ntcrash.C.A@4]" +main:2: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.A@4]" +main:3: note: Revealed type is "tuple[builtins.int, fallback=ntcrash.A@4]" +main:4: note: Revealed type is "def (x: builtins.int) -> tuple[builtins.int, fallback=ntcrash.A@4]" -- -- Strict optional @@ -1066,13 +1066,13 @@ class C: [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] [out1] -main:2: note: Revealed type is "TypedDict('ntcrash.C.A@4', {'x': builtins.int})" -main:3: note: Revealed type is "TypedDict('ntcrash.C.A@4', {'x': builtins.int})" -main:4: note: Revealed type is "def (*, x: builtins.int) -> TypedDict('ntcrash.C.A@4', {'x': builtins.int})" +main:2: note: Revealed type is "TypedDict('ntcrash.A@4', {'x': builtins.int})" +main:3: note: Revealed type is "TypedDict('ntcrash.A@4', {'x': builtins.int})" +main:4: note: Revealed type is "def (*, x: builtins.int) -> TypedDict('ntcrash.A@4', {'x': builtins.int})" [out2] -main:2: note: Revealed type is "TypedDict('ntcrash.C.A@4', {'x': builtins.int})" -main:3: note: Revealed type is "TypedDict('ntcrash.C.A@4', {'x': builtins.int})" -main:4: note: Revealed type is "def (*, x: builtins.int) -> TypedDict('ntcrash.C.A@4', {'x': builtins.int})" +main:2: note: Revealed type is "TypedDict('ntcrash.A@4', {'x': builtins.int})" +main:3: note: Revealed type is "TypedDict('ntcrash.A@4', {'x': builtins.int})" +main:4: note: Revealed type is "def (*, x: builtins.int) -> TypedDict('ntcrash.A@4', {'x': builtins.int})" [case testSerializeNonTotalTypedDict] from m import d diff --git a/test-data/unit/semanal-namedtuple.test b/test-data/unit/semanal-namedtuple.test index 62bd87f1995a..7f5d7b6afb63 100644 --- a/test-data/unit/semanal-namedtuple.test +++ b/test-data/unit/semanal-namedtuple.test @@ -121,9 +121,9 @@ MypyFile:1( ClassDef:2( A TupleType( - tuple[Any, fallback=__main__.N@2]) + tuple[Any, fallback=__main__.namedtuple@N]) BaseType( - __main__.N@2) + __main__.namedtuple@N) PassStmt:2())) [case testNamedTupleBaseClassWithItemTypes] @@ -136,9 +136,9 @@ MypyFile:1( ClassDef:2( A TupleType( - tuple[builtins.int, fallback=__main__.N@2]) + tuple[builtins.int, fallback=__main__.namedtuple@N]) BaseType( - __main__.N@2) + __main__.namedtuple@N) PassStmt:2())) -- Errors @@ -239,9 +239,9 @@ MypyFile:1( ClassDef:4( A TupleType( - tuple[builtins.int, fallback=__main__.N@4]) + tuple[builtins.int, fallback=__main__.namedtuple@N]) Decorators( NameExpr(final [typing.final])) BaseType( - __main__.N@4) + __main__.namedtuple@N) PassStmt:5()))