void (@NotNull PsiScopeProcessor processor, @NotNull PsiElement element, @Nullable String name, @Nullable PsiElement roof) { ScopeOwner originalOwner = ScopeUtil.getScopeOwner(element); PsiElement parent = element.getParent(); ScopeOwner owner = originalOwner; if (parent instanceof PyNonlocalStatement) { /* wee need to search in one step out scope for nonlocal statements */ final ScopeOwner outerScopeOwner = ScopeUtil.getScopeOwner(owner); if (outerScopeOwner != null) { owner = outerScopeOwner; } } else if (parent instanceof PyGlobalStatement) { /* wee need to search directly in global scope of the module for global statements */ final PsiFile globalScope = element.getContainingFile(); if (globalScope instanceof PyFile) { owner = (PyFile)globalScope; } } if (owner instanceof PyFunction function && function.getTypeParameterList() != null) { // Type parameters of generic functions and methods need to be visible in their parameter and return type annotations, // so we resolve names in these annotations starting from the containing function's scope, "lowering" their scope in // ScopeUtil.getScopeOwner(). // At the same time, these annotations still need to see names from the class scope, such as nested classes defined there, // as if these names were evaluated on a class-level scope. PyAnnotation annotation = PsiTreeUtil.getParentOfType(element, PyAnnotation.class); if (PsiTreeUtil.getParentOfType(annotation, PyFunction.class, true, PyStatement.class) == function) { originalOwner = ScopeUtil.getScopeOwner(function); } } if (owner instanceof PyClass pyClass && pyClass.getTypeParameterList() != null) { // The same logic applies to the list of base classes of a generic class PyArgumentList superclassList = PsiTreeUtil.getParentOfType(element, PyArgumentList.class); if (superclassList != null && superclassList.getParent() == pyClass) { originalOwner = ScopeUtil.getScopeOwner(pyClass); } } scopeCrawlUp(processor, owner, originalOwner, name, roof); }