diff --git a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index 6440b2a88217..1fae88afd800 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/python-psi-impl/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -1050,6 +1050,9 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { myBuilder.addNodeAndCheckPending(instruction); } + @Override + public void visitPyTypeParameterList(@NotNull PyTypeParameterList node) { } + private void visitCondition(@NotNull PyExpression expression, @NotNull Instruction trueNode, @NotNull Instruction falseNode) { TrueFalseNodes prevTrueFalseNodes = myTrueFalseNodes; myTrueFalseNodes = new TrueFalseNodes(trueNode, falseNode); diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java index fffbd73c062c..20ff67c94233 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java @@ -114,6 +114,10 @@ public final class PyUnboundLocalVariableInspection extends PyInspection { } final PsiPolyVariantReference ref = node.getReference(getResolveContext()); final PsiElement resolved = ref.resolve(); + // type parameter list is not included in CFG + if (resolved instanceof PyTypeParameter) { + return; + } final boolean isBuiltin = PyBuiltinCache.getInstance(node).isBuiltin(resolved); if (owner instanceof PyClass) { if (isBuiltin || ScopeUtil.getDeclarationScopeOwner(owner, name) != null) { diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/unusedLocal/PyUnusedLocalInspectionVisitor.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/unusedLocal/PyUnusedLocalInspectionVisitor.java index 85ed887def4a..782ed05a3893 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/unusedLocal/PyUnusedLocalInspectionVisitor.java +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/unusedLocal/PyUnusedLocalInspectionVisitor.java @@ -97,8 +97,15 @@ public final class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor { } private void collectAllWrites(ScopeOwner owner) { - final Instruction[] instructions = ControlFlowCache.getControlFlow(owner).getInstructions(); Set scopeWrites = new HashSet<>(); + // type parameter list is not included in CFG + if (owner instanceof PyTypeParameterListOwner typeParameterListOwner) { + PyTypeParameterList typeParameterList = typeParameterListOwner.getTypeParameterList(); + if (typeParameterList != null) { + scopeWrites.addAll(typeParameterList.getTypeParameters()); + } + } + final Instruction[] instructions = ControlFlowCache.getControlFlow(owner).getInstructions(); for (Instruction instruction : instructions) { final PsiElement element = instruction.getElement(); if (element instanceof PyFunction && owner instanceof PyFunction) { diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index 33eb7ab8f122..c88970062abb 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -236,7 +236,10 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference final ScopeOwner resolvedOwner = processor.getOwner(); final Collection resolvedElements = processor.getElements(); - if (resolvedOwner != null && !resolvedElements.isEmpty() && !ControlFlowCache.getScope(resolvedOwner).isGlobal(referencedName)) { + if (resolvedOwner != null && + !processor.isTypeParameterScope() && + !resolvedElements.isEmpty() && + !ControlFlowCache.getScope(resolvedOwner).isGlobal(referencedName)) { if (resolvedOwner == referenceOwner && referenceAnchor != null) { final List instructions = getLatestDefinitions(referencedName, resolvedOwner, referenceAnchor); // TODO: Use the results from the processor as a cache for resolving to latest defs diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/PyResolveProcessor.java b/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/PyResolveProcessor.java index 5a2fce3359ab..77ce93fccabb 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/PyResolveProcessor.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/PyResolveProcessor.java @@ -8,10 +8,7 @@ import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; -import com.jetbrains.python.psi.PyFromImportStatement; -import com.jetbrains.python.psi.PyImportElement; -import com.jetbrains.python.psi.PyImportedNameDefiner; -import com.jetbrains.python.psi.PyUtil; +import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.ResolveResultList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,6 +24,7 @@ public class PyResolveProcessor implements PsiScopeProcessor { private final @NotNull Map myResults = Maps.newLinkedHashMap(); private final @NotNull Map myImplicitlyImportedResults = Maps.newLinkedHashMap(); protected @Nullable ScopeOwner myOwner; + private boolean myTypeParameterScope; public PyResolveProcessor(@NotNull String name) { this(name, false); @@ -79,6 +77,10 @@ public class PyResolveProcessor implements PsiScopeProcessor { return myOwner; } + public boolean isTypeParameterScope() { + return myTypeParameterScope; + } + private @NotNull List resolveInImportedNameDefiner(@NotNull PyImportedNameDefiner definer) { if (myLocalResolve) { final PyImportElement importElement = PyUtil.as(definer, PyImportElement.class); @@ -96,8 +98,9 @@ public class PyResolveProcessor implements PsiScopeProcessor { final ScopeOwner owner = ScopeUtil.getScopeOwner(definer != null ? definer : element); if (myOwner == null) { myOwner = owner; + myTypeParameterScope = element instanceof PyTypeParameter; } - final boolean sameScope = owner == myOwner; + final boolean sameScope = owner == myOwner && (element instanceof PyTypeParameter) == myTypeParameterScope; if (sameScope) { addResult(element, definer); } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java b/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java index 7af943123ac2..e39fb9194b4d 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.psi.resolve; +import com.google.common.collect.Iterables; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; @@ -117,7 +118,10 @@ public final class PyResolveUtil { else { namedElements = scope.getNamedElements(); } - for (PsiElement element : ContainerUtil.concat(namedElements, scope.getImportedNameDefiners())) { + Iterable elements = Iterables.concat(Iterables.filter(namedElements, t -> !(t instanceof PyTypeParameter)), + scope.getImportedNameDefiners(), + Iterables.filter(namedElements, PyTypeParameter.class)); + for (PsiElement element : elements) { if (isClassLevelDefinitionInvisibleToReference(element, scopeOwner, originalScopeOwner)) continue; if (!processor.execute(element, ResolveState.initial())) { return; diff --git a/python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreIncludedInItsGraph.txt b/python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreIncludedInItsGraph.txt deleted file mode 100644 index 3fc5784c56ac..000000000000 --- a/python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreIncludedInItsGraph.txt +++ /dev/null @@ -1,12 +0,0 @@ -0(1) element: null -1(2) element: PyTypeParameter -2(3) WRITE ACCESS: T -3(4) element: PySubscriptionExpression -4(5) READ ACCESS: list -5(6) READ ACCESS: T -6(7) WRITE ACCESS: x -7(8) READ ACCESS: T -8(9) element: PyReturnStatement -9(10) element: PySubscriptionExpression -10(11) READ ACCESS: x -11() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreIncludedInItsGraph.py b/python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreNotIncludedInItsGraph.py similarity index 100% rename from python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreIncludedInItsGraph.py rename to python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreNotIncludedInItsGraph.py diff --git a/python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreNotIncludedInItsGraph.txt b/python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreNotIncludedInItsGraph.txt new file mode 100644 index 000000000000..b2d4cf63d233 --- /dev/null +++ b/python/testData/codeInsight/controlflow/NewStyleGenericFunctionAnnotationsAreNotIncludedInItsGraph.txt @@ -0,0 +1,10 @@ +0(1) element: null +1(2) element: PySubscriptionExpression +2(3) READ ACCESS: list +3(4) READ ACCESS: T +4(5) WRITE ACCESS: x +5(6) READ ACCESS: T +6(7) element: PyReturnStatement +7(8) element: PySubscriptionExpression +8(9) READ ACCESS: x +9() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/TypeAliasStatementWithTypeParameterList.txt b/python/testData/codeInsight/controlflow/TypeAliasStatementWithTypeParameterList.txt index 83c0d790a166..6119222c3f97 100644 --- a/python/testData/codeInsight/controlflow/TypeAliasStatementWithTypeParameterList.txt +++ b/python/testData/codeInsight/controlflow/TypeAliasStatementWithTypeParameterList.txt @@ -1,12 +1,6 @@ 0(1) element: null -1(2) element: PyTypeParameter -2(3) WRITE ACCESS: T -3(4) READ ACCESS: str -4(5) element: PyTypeParameter -5(6) WRITE ACCESS: U -6(7) READ ACCESS: int -7(8) element: PySubscriptionExpression -8(9) READ ACCESS: Union -9(10) READ ACCESS: T -10(11) READ ACCESS: U -11() element: null \ No newline at end of file +1(2) element: PySubscriptionExpression +2(3) READ ACCESS: Union +3(4) READ ACCESS: T +4(5) READ ACCESS: U +5() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/TypeParameterListInClassDeclaration.txt b/python/testData/codeInsight/controlflow/TypeParameterListInClassDeclaration.txt index 1387f6362f3c..8396b7ff64c6 100644 --- a/python/testData/codeInsight/controlflow/TypeParameterListInClassDeclaration.txt +++ b/python/testData/codeInsight/controlflow/TypeParameterListInClassDeclaration.txt @@ -1,10 +1,6 @@ 0(1) element: null -1(2) element: PyTypeParameter -2(3) WRITE ACCESS: T -3(4) element: PyTypeParameter -4(5) WRITE ACCESS: U -5(6) element: PySubscriptionExpression -6(7) READ ACCESS: BaseClass -7(8) READ ACCESS: T -8(9) element: PyPassStatement -9() element: null \ No newline at end of file +1(2) element: PySubscriptionExpression +2(3) READ ACCESS: BaseClass +3(4) READ ACCESS: T +4(5) element: PyPassStatement +5() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/TypeParameterListInFunctionDeclaration.txt b/python/testData/codeInsight/controlflow/TypeParameterListInFunctionDeclaration.txt index d55fa5031f7e..3fe1322fc4dd 100644 --- a/python/testData/codeInsight/controlflow/TypeParameterListInFunctionDeclaration.txt +++ b/python/testData/codeInsight/controlflow/TypeParameterListInFunctionDeclaration.txt @@ -1,11 +1,7 @@ 0(1) element: null -1(2) element: PyTypeParameter -2(3) WRITE ACCESS: T -3(4) element: PyTypeParameter -4(5) WRITE ACCESS: U -5(6) READ ACCESS: T -6(7) WRITE ACCESS: a -7(8) READ ACCESS: U -8(9) WRITE ACCESS: b -9(10) element: PyPassStatement -10() element: null \ No newline at end of file +1(2) READ ACCESS: T +2(3) WRITE ACCESS: a +3(4) READ ACCESS: U +4(5) WRITE ACCESS: b +5(6) element: PyPassStatement +6() element: null \ No newline at end of file diff --git a/python/testData/resolve/TypeParameterRebindToLocalVariableInEnclosingScope.py b/python/testData/resolve/TypeParameterRebindToLocalVariableInEnclosingScope.py new file mode 100644 index 000000000000..05ceab6f1210 --- /dev/null +++ b/python/testData/resolve/TypeParameterRebindToLocalVariableInEnclosingScope.py @@ -0,0 +1,6 @@ +def outer[T](): + def inner(): + print(T) +# + + T = -1 \ No newline at end of file diff --git a/python/testData/resolve/TypeParameterRebindToLocalVariableInSameScope.py b/python/testData/resolve/TypeParameterRebindToLocalVariableInSameScope.py new file mode 100644 index 000000000000..70044d902ad8 --- /dev/null +++ b/python/testData/resolve/TypeParameterRebindToLocalVariableInSameScope.py @@ -0,0 +1,4 @@ +def f[T](): + print(T) +# + T = 1 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/Py3ResolveTest.java b/python/testSrc/com/jetbrains/python/Py3ResolveTest.java index 7d55d9e7b98e..7d95b46cb48d 100644 --- a/python/testSrc/com/jetbrains/python/Py3ResolveTest.java +++ b/python/testSrc/com/jetbrains/python/Py3ResolveTest.java @@ -895,6 +895,16 @@ public class Py3ResolveTest extends PyResolveTestCase { assertResolvesToItself(); } + // PY-82699 + public void testTypeParameterRebindToLocalVariableInEnclosingScope() { + assertResolvesTo(PyTargetExpression.class, "T"); + } + + // PY-82699 + public void testTypeParameterRebindToLocalVariableInSameScope() { + assertUnresolved(); + } + private void assertResolvesToItself() { PsiElement resolved = doResolve(); PsiReference reference = PyResolveTestCase.findReferenceByMarker(myFixture.getFile()); diff --git a/python/testSrc/com/jetbrains/python/Py3TypeTest.java b/python/testSrc/com/jetbrains/python/Py3TypeTest.java index 8aa36c3e6283..0e3f5bec02ac 100644 --- a/python/testSrc/com/jetbrains/python/Py3TypeTest.java +++ b/python/testSrc/com/jetbrains/python/Py3TypeTest.java @@ -3933,6 +3933,17 @@ public class Py3TypeTest extends PyTestCase { """); } + // PY-82699 + public void testTypeParameterRebind() { + doTest("int", """ + def outer[T]() -> None: + def inner() -> None: + expr = T + + T = -1 + """); + } + private void doTest(final String expectedType, final String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class); diff --git a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java index 11a595e087e7..ca060bcff1d1 100644 --- a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java +++ b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java @@ -578,8 +578,8 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase { doTest(); } - // PY-61877 - public void testNewStyleGenericFunctionAnnotationsAreIncludedInItsGraph() { + // PY-61877 PY-82699 + public void testNewStyleGenericFunctionAnnotationsAreNotIncludedInItsGraph() { doTestFirstStatement(); } diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java index c93fa1324969..453540b9383f 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java @@ -464,4 +464,16 @@ public class Py3UnresolvedReferencesInspectionTest extends PyInspectionTestCase ); } + // PY-82699 + public void testTypeParameterRebind() { + doTestByText(""" + def outer1[T]() -> None: + print(T) + T = 1 + + def outer2[T]() -> None: + print(T) + """); + } + }