@@ -577,6 +577,10 @@ def unused_annotations(self):
577577 yield name , binding
578578
579579
580+ class TypeScope (Scope ):
581+ pass
582+
583+
580584class GeneratorScope (Scope ):
581585 pass
582586
@@ -1039,15 +1043,20 @@ def handleNodeLoad(self, node, parent):
10391043 if not name :
10401044 return
10411045
1042- in_generators = None
1046+ # only the following can access class scoped variables (since classes
1047+ # aren't really a scope)
1048+ # - direct accesses (not within a nested scope)
1049+ # - generators
1050+ # - type annotations (for generics, etc.)
1051+ can_access_class_vars = None
10431052 importStarred = None
10441053
10451054 # try enclosing function scopes and global scope
10461055 for scope in self .scopeStack [- 1 ::- 1 ]:
10471056 if isinstance (scope , ClassScope ):
10481057 if name == '__class__' :
10491058 return
1050- elif in_generators is False :
1059+ elif can_access_class_vars is False :
10511060 # only generators used in a class scope can access the
10521061 # names of the class. this is skipped during the first
10531062 # iteration
@@ -1082,8 +1091,10 @@ def handleNodeLoad(self, node, parent):
10821091
10831092 importStarred = importStarred or scope .importStarred
10841093
1085- if in_generators is not False :
1086- in_generators = isinstance (scope , GeneratorScope )
1094+ if can_access_class_vars is not False :
1095+ can_access_class_vars = isinstance (
1096+ scope , (TypeScope , GeneratorScope ),
1097+ )
10871098
10881099 if importStarred :
10891100 from_list = []
@@ -1310,6 +1321,10 @@ def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err):
13101321
13111322 self .handleNode (parsed_annotation , node )
13121323
1324+ def handle_annotation_always_deferred (self , annotation , parent ):
1325+ fn = in_annotation (Checker .handleNode )
1326+ self .deferFunction (lambda : fn (self , annotation , parent ))
1327+
13131328 @in_annotation
13141329 def handleAnnotation (self , annotation , node ):
13151330 if (
@@ -1326,8 +1341,7 @@ def handleAnnotation(self, annotation, node):
13261341 messages .ForwardAnnotationSyntaxError ,
13271342 ))
13281343 elif self .annotationsFutureEnabled :
1329- fn = in_annotation (Checker .handleNode )
1330- self .deferFunction (lambda : fn (self , annotation , node ))
1344+ self .handle_annotation_always_deferred (annotation , node )
13311345 else :
13321346 self .handleNode (annotation , node )
13331347
@@ -1902,7 +1916,10 @@ def YIELD(self, node):
19021916 def FUNCTIONDEF (self , node ):
19031917 for deco in node .decorator_list :
19041918 self .handleNode (deco , node )
1905- self .LAMBDA (node )
1919+
1920+ with self ._type_param_scope (node ):
1921+ self .LAMBDA (node )
1922+
19061923 self .addBinding (node , FunctionDefinition (node .name , node ))
19071924 # doctest does not process doctest within a doctest,
19081925 # or in nested functions.
@@ -1951,7 +1968,10 @@ def LAMBDA(self, node):
19511968
19521969 def runFunction ():
19531970 with self .in_scope (FunctionScope ):
1954- self .handleChildren (node , omit = ['decorator_list' , 'returns' ])
1971+ self .handleChildren (
1972+ node ,
1973+ omit = ('decorator_list' , 'returns' , 'type_params' ),
1974+ )
19551975
19561976 self .deferFunction (runFunction )
19571977
@@ -1969,19 +1989,22 @@ def CLASSDEF(self, node):
19691989 """
19701990 for deco in node .decorator_list :
19711991 self .handleNode (deco , node )
1972- for baseNode in node .bases :
1973- self .handleNode (baseNode , node )
1974- for keywordNode in node .keywords :
1975- self .handleNode (keywordNode , node )
1976- with self .in_scope (ClassScope ):
1977- # doctest does not process doctest within a doctest
1978- # classes within classes are processed.
1979- if (self .withDoctest and
1980- not self ._in_doctest () and
1981- not isinstance (self .scope , FunctionScope )):
1982- self .deferFunction (lambda : self .handleDoctests (node ))
1983- for stmt in node .body :
1984- self .handleNode (stmt , node )
1992+
1993+ with self ._type_param_scope (node ):
1994+ for baseNode in node .bases :
1995+ self .handleNode (baseNode , node )
1996+ for keywordNode in node .keywords :
1997+ self .handleNode (keywordNode , node )
1998+ with self .in_scope (ClassScope ):
1999+ # doctest does not process doctest within a doctest
2000+ # classes within classes are processed.
2001+ if (self .withDoctest and
2002+ not self ._in_doctest () and
2003+ not isinstance (self .scope , FunctionScope )):
2004+ self .deferFunction (lambda : self .handleDoctests (node ))
2005+ for stmt in node .body :
2006+ self .handleNode (stmt , node )
2007+
19852008 self .addBinding (node , ClassDefinition (node .name , node ))
19862009
19872010 def AUGASSIGN (self , node ):
@@ -2155,3 +2178,21 @@ def _match_target(self, node):
21552178 self .handleChildren (node )
21562179
21572180 MATCHAS = MATCHMAPPING = MATCHSTAR = _match_target
2181+
2182+ @contextlib .contextmanager
2183+ def _type_param_scope (self , node ):
2184+ with contextlib .ExitStack () as ctx :
2185+ if sys .version_info >= (3 , 12 ):
2186+ ctx .enter_context (self .in_scope (TypeScope ))
2187+ for param in node .type_params :
2188+ self .handleNode (param , node )
2189+ yield
2190+
2191+ def TYPEVAR (self , node ):
2192+ self .handleNodeStore (node )
2193+ self .handle_annotation_always_deferred (node .bound , node )
2194+
2195+ def TYPEALIAS (self , node ):
2196+ self .handleNode (node .name , node )
2197+ with self ._type_param_scope (node ):
2198+ self .handle_annotation_always_deferred (node .value , node )
0 commit comments