77import time
88from collections import defaultdict
99from collections .abc import Iterable , Iterator , Sequence
10- from contextlib import contextmanager
10+ from contextlib import contextmanager , nullcontext
1111from typing import Callable , ClassVar , Final , Optional , cast , overload
1212from typing_extensions import TypeAlias as _TypeAlias , assert_never
1313
9494 TypedDictExpr ,
9595 TypeInfo ,
9696 TypeVarExpr ,
97+ TypeVarLikeExpr ,
9798 TypeVarTupleExpr ,
9899 UnaryExpr ,
99100 Var ,
173174 TypeOfAny ,
174175 TypeType ,
175176 TypeVarId ,
177+ TypeVarLikeType ,
176178 TypeVarTupleType ,
177179 TypeVarType ,
178180 UnboundType ,
@@ -377,26 +379,24 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
377379 result = self .analyze_var_ref (node , e )
378380 if isinstance (result , PartialType ):
379381 result = self .chk .handle_partial_var_type (result , lvalue , node , e )
380- elif isinstance (node , FuncDef ):
381- # Reference to a global function.
382- result = function_type (node , self .named_type ("builtins.function" ))
382+ elif isinstance (node , Decorator ):
383+ result = self .analyze_var_ref (node .var , e )
383384 elif isinstance (node , OverloadedFuncDef ):
384385 if node .type is None :
385386 if self .chk .in_checked_function () and node .items :
386387 self .chk .handle_cannot_determine_type (node .name , e )
387388 result = AnyType (TypeOfAny .from_error )
388389 else :
389390 result = node .type
390- elif isinstance (node , TypeInfo ):
391- # Reference to a type object.
392- if node .typeddict_type :
393- # We special-case TypedDict, because they don't define any constructor.
394- result = self .typeddict_callable (node )
395- elif node .fullname == "types.NoneType" :
396- # We special case NoneType, because its stub definition is not related to None.
397- result = TypeType (NoneType ())
398- else :
399- result = type_object_type (node , self .named_type )
391+ elif isinstance (node , (FuncDef , TypeInfo , TypeAlias , MypyFile , TypeVarLikeExpr )):
392+ result = self .analyze_static_reference (node , e , e .is_alias_rvalue or lvalue )
393+ else :
394+ if isinstance (node , PlaceholderNode ):
395+ assert False , f"PlaceholderNode { node .fullname !r} leaked to checker"
396+ # Unknown reference; use any type implicitly to avoid
397+ # generating extra type errors.
398+ result = AnyType (TypeOfAny .from_error )
399+ if isinstance (node , TypeInfo ):
400400 if isinstance (result , CallableType ) and isinstance ( # type: ignore[misc]
401401 result .ret_type , Instance
402402 ):
@@ -408,30 +408,56 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
408408 # This is the type in a type[] expression, so substitute type
409409 # variables with Any.
410410 result = erasetype .erase_typevars (result )
411- elif isinstance (node , MypyFile ):
412- # Reference to a module object.
413- result = self .module_type (node )
414- elif isinstance (node , Decorator ):
415- result = self .analyze_var_ref (node .var , e )
411+ assert result is not None
412+ return result
413+
414+ def analyze_static_reference (
415+ self ,
416+ node : SymbolNode ,
417+ ctx : Context ,
418+ is_lvalue : bool ,
419+ * ,
420+ include_modules : bool = True ,
421+ suppress_errors : bool = False ,
422+ ) -> Type :
423+ """
424+ This is the version of analyze_ref_expr() that doesn't do any deferrals.
425+
426+ This function can be used by member access to "static" attributes. For example,
427+ when accessing module attributes in protocol checks, or accessing attributes of
428+ special kinds (like TypeAlias, TypeInfo, etc.) on an instance or class object.
429+ # TODO: merge with analyze_ref_expr() when we are confident about performance.
430+ """
431+ if isinstance (node , (Var , Decorator , OverloadedFuncDef )):
432+ return node .type or AnyType (TypeOfAny .special_form )
433+ elif isinstance (node , FuncDef ):
434+ return function_type (node , self .named_type ("builtins.function" ))
435+ elif isinstance (node , TypeInfo ):
436+ # Reference to a type object.
437+ if node .typeddict_type :
438+ # We special-case TypedDict, because they don't define any constructor.
439+ return self .typeddict_callable (node )
440+ elif node .fullname == "types.NoneType" :
441+ # We special case NoneType, because its stub definition is not related to None.
442+ return TypeType (NoneType ())
443+ else :
444+ return type_object_type (node , self .named_type )
416445 elif isinstance (node , TypeAlias ):
417446 # Something that refers to a type alias appears in runtime context.
418447 # Note that we suppress bogus errors for alias redefinitions,
419448 # they are already reported in semanal.py.
420- result = self .alias_type_in_runtime_context (
421- node , ctx = e , alias_definition = e .is_alias_rvalue or lvalue
422- )
449+ with self .msg .filter_errors () if suppress_errors else nullcontext ():
450+ return self .alias_type_in_runtime_context (
451+ node , ctx = ctx , alias_definition = is_lvalue
452+ )
423453 elif isinstance (node , TypeVarExpr ):
424454 return self .named_type ("typing.TypeVar" )
425455 elif isinstance (node , (ParamSpecExpr , TypeVarTupleExpr )):
426- result = self .object_type ()
427- else :
428- if isinstance (node , PlaceholderNode ):
429- assert False , f"PlaceholderNode { node .fullname !r} leaked to checker"
430- # Unknown reference; use any type implicitly to avoid
431- # generating extra type errors.
432- result = AnyType (TypeOfAny .from_error )
433- assert result is not None
434- return result
456+ return self .object_type ()
457+ elif isinstance (node , MypyFile ):
458+ # Reference to a module object.
459+ return self .module_type (node ) if include_modules else AnyType (TypeOfAny .special_form )
460+ return AnyType (TypeOfAny .from_error )
435461
436462 def analyze_var_ref (self , var : Var , context : Context ) -> Type :
437463 if var .type :
@@ -459,20 +485,21 @@ def module_type(self, node: MypyFile) -> Instance:
459485 # Fall back to a dummy 'object' type instead to
460486 # avoid a crash.
461487 result = self .named_type ("builtins.object" )
462- module_attrs = {}
488+ module_attrs : dict [ str , Type ] = {}
463489 immutable = set ()
464490 for name , n in node .names .items ():
465491 if not n .module_public :
466492 continue
467493 if isinstance (n .node , Var ) and n .node .is_final :
468494 immutable .add (name )
469- typ = self .chk .determine_type_of_member (n )
470- if typ :
471- module_attrs [name ] = typ
495+ if n .node is None :
496+ module_attrs [name ] = AnyType (TypeOfAny .from_error )
472497 else :
473498 # TODO: what to do about nested module references?
474499 # They are non-trivial because there may be import cycles.
475- module_attrs [name ] = AnyType (TypeOfAny .special_form )
500+ module_attrs [name ] = self .analyze_static_reference (
501+ n .node , n .node , False , include_modules = False , suppress_errors = True
502+ )
476503 result .extra_attrs = ExtraAttrs (module_attrs , immutable , node .fullname )
477504 return result
478505
@@ -961,19 +988,11 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType:
961988 assert info .special_alias is not None
962989 target = info .special_alias .target
963990 assert isinstance (target , ProperType ) and isinstance (target , TypedDictType )
964- expected_types = list (target .items .values ())
965- kinds = [ArgKind .ARG_NAMED ] * len (expected_types )
966- names = list (target .items .keys ())
967- return CallableType (
968- expected_types ,
969- kinds ,
970- names ,
971- target ,
972- self .named_type ("builtins.type" ),
973- variables = info .defn .type_vars ,
974- )
991+ return self .typeddict_callable_from_context (target , info .defn .type_vars )
975992
976- def typeddict_callable_from_context (self , callee : TypedDictType ) -> CallableType :
993+ def typeddict_callable_from_context (
994+ self , callee : TypedDictType , variables : Sequence [TypeVarLikeType ] | None = None
995+ ) -> CallableType :
977996 return CallableType (
978997 list (callee .items .values ()),
979998 [
@@ -983,6 +1002,8 @@ def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType
9831002 list (callee .items .keys ()),
9841003 callee ,
9851004 self .named_type ("builtins.type" ),
1005+ variables = variables ,
1006+ is_bound = True ,
9861007 )
9871008
9881009 def check_typeddict_call_with_kwargs (
0 commit comments