diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index baac51970a97..c28e78734eae 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -512,20 +512,19 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return true; } if (PsiTreeUtil.isAncestor(theirContainer, ourContainer, true)) { - if (ourScopeOwner != theirScopeOwner) { - boolean shadowsName = false; - ScopeOwner owner = ourScopeOwner; - while(owner != theirScopeOwner && owner != null) { - if (ControlFlowCache.getScope(owner).containsDeclaration(elementName)) { - shadowsName = true; - break; - } - owner = ScopeUtil.getScopeOwner(owner); - } - if (!shadowsName) { - return true; - } + if (ourContainer instanceof PyComprehensionElement && containsDeclaration((PyComprehensionElement)ourContainer, elementName)) { + return false; } + + ScopeOwner owner = ourScopeOwner; + while (owner != theirScopeOwner && owner != null) { + if (ControlFlowCache.getScope(owner).containsDeclaration(elementName)) { + return false; + } + owner = ScopeUtil.getScopeOwner(owner); + } + + return true; } } return false; @@ -540,6 +539,25 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return parent; } + private static boolean containsDeclaration(@NotNull PyComprehensionElement comprehensionElement, @NotNull String variableName) { + for (ComprhForComponent forComponent : comprehensionElement.getForComponents()) { + final PyExpression iteratorVariable = forComponent.getIteratorVariable(); + + if (iteratorVariable instanceof PyTupleExpression) { + for (PyExpression variable : (PyTupleExpression)iteratorVariable) { + if (variable instanceof PyTargetExpression && variableName.equals(variable.getName())) { + return true; + } + } + } + else if (iteratorVariable instanceof PyTargetExpression && variableName.equals(iteratorVariable.getName())) { + return true; + } + } + + return false; + } + private boolean resolvesToSameGlobal(PsiElement element, String elementName, ScopeOwner ourScopeOwner, ScopeOwner theirScopeOwner, PsiElement resolveResult) { // Handle situations when there is no top-level declaration for globals and transitive resolve doesn't help diff --git a/python/testData/findUsages/OuterVariableInGenerator.py b/python/testData/findUsages/OuterVariableInGenerator.py new file mode 100644 index 000000000000..5007fd9a95d5 --- /dev/null +++ b/python/testData/findUsages/OuterVariableInGenerator.py @@ -0,0 +1,6 @@ +foo = [1, 2] +foo = [bar for bar in foo] +bar = dict( + (baz, baz) + for baz in foo +) \ No newline at end of file diff --git a/python/testData/findUsages/OuterVariableInListComprehension.py b/python/testData/findUsages/OuterVariableInListComprehension.py new file mode 100644 index 000000000000..9649bc0bc863 --- /dev/null +++ b/python/testData/findUsages/OuterVariableInListComprehension.py @@ -0,0 +1,5 @@ +def f(): + xst = [(1, 2)] + print(xst) + xst = [(3, 4)] + return [(k, str(v)) for k, v in xst] \ No newline at end of file diff --git a/python/testData/findUsages/OverrideVariableByTupleInComprehension1.py b/python/testData/findUsages/OverrideVariableByTupleInComprehension1.py new file mode 100644 index 000000000000..31584e0029d3 --- /dev/null +++ b/python/testData/findUsages/OverrideVariableByTupleInComprehension1.py @@ -0,0 +1,4 @@ +def f(): + xst = [(3, 4)] + k = 3 + return [(k, str(v)) for k, v in xst] \ No newline at end of file diff --git a/python/testData/findUsages/OverrideVariableByTupleInComprehension2.py b/python/testData/findUsages/OverrideVariableByTupleInComprehension2.py new file mode 100644 index 000000000000..2bc5df159777 --- /dev/null +++ b/python/testData/findUsages/OverrideVariableByTupleInComprehension2.py @@ -0,0 +1,4 @@ +def f(): + xst = [(3, 4)] + k = 3 + return [(k, str(v)) for k, v in xst] \ No newline at end of file diff --git a/python/testData/findUsages/OverrideVariableInComprehension1.py b/python/testData/findUsages/OverrideVariableInComprehension1.py new file mode 100644 index 000000000000..f1d3d4868ce1 --- /dev/null +++ b/python/testData/findUsages/OverrideVariableInComprehension1.py @@ -0,0 +1,4 @@ +def f(): + xst = [3, 4] + k = 3 + return [k for k in xst] \ No newline at end of file diff --git a/python/testData/findUsages/OverrideVariableInComprehension2.py b/python/testData/findUsages/OverrideVariableInComprehension2.py new file mode 100644 index 000000000000..a32aa3e0bf41 --- /dev/null +++ b/python/testData/findUsages/OverrideVariableInComprehension2.py @@ -0,0 +1,4 @@ +def f(): + xst = [3, 4] + k = 3 + return [k for k in xst] \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyFindUsagesTest.java b/python/testSrc/com/jetbrains/python/PyFindUsagesTest.java index 9cd48c2342a2..2e288b4e01a2 100644 --- a/python/testSrc/com/jetbrains/python/PyFindUsagesTest.java +++ b/python/testSrc/com/jetbrains/python/PyFindUsagesTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -148,6 +148,86 @@ public class PyFindUsagesTest extends PyTestCase { assertEquals(3, usages.size()); } + // PY-8604 + public void testOuterVariableInGeneratorPy2() { + runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() { + @Override + public void run() { + assertEquals(4, myFixture.testFindUsages("findUsages/OuterVariableInGenerator.py").size()); + } + }); + } + + // PY-8604 + public void testOuterVariableInGeneratorPy3() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + assertEquals(4, myFixture.testFindUsages("findUsages/OuterVariableInGenerator.py").size()); + } + }); + } + + // PY-18808 + public void testOuterVariableInListComprehensionPy2() { + runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() { + @Override + public void run() { + assertEquals(4, myFixture.testFindUsages("findUsages/OuterVariableInListComprehension.py").size()); + } + }); + } + + // PY-18808 + public void testOuterVariableInListComprehensionPy3() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + assertEquals(4, myFixture.testFindUsages("findUsages/OuterVariableInListComprehension.py").size()); + } + }); + } + + public void testOverrideVariableByTupleInComprehensionPy2() { + runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() { + @Override + public void run() { + assertEquals(3, myFixture.testFindUsages("findUsages/OverrideVariableByTupleInComprehension1.py").size()); + assertEquals(3, myFixture.testFindUsages("findUsages/OverrideVariableByTupleInComprehension2.py").size()); + } + }); + } + + public void testOverrideVariableByTupleInComprehensionPy3() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + assertEquals(1, myFixture.testFindUsages("findUsages/OverrideVariableByTupleInComprehension1.py").size()); + assertEquals(2, myFixture.testFindUsages("findUsages/OverrideVariableByTupleInComprehension2.py").size()); + } + }); + } + + public void testOverrideVariableInComprehensionPy2() { + runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() { + @Override + public void run() { + assertEquals(3, myFixture.testFindUsages("findUsages/OverrideVariableInComprehension1.py").size()); + assertEquals(3, myFixture.testFindUsages("findUsages/OverrideVariableInComprehension2.py").size()); + } + }); + } + + public void testOverrideVariableInComprehensionPy3() { + runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() { + @Override + public void run() { + assertEquals(1, myFixture.testFindUsages("findUsages/OverrideVariableInComprehension1.py").size()); + assertEquals(2, myFixture.testFindUsages("findUsages/OverrideVariableInComprehension2.py").size()); + } + }); + } + private Collection findMultiFileUsages(String filename) { final String testName = getTestName(false); myFixture.copyDirectoryToProject("findUsages/" + testName, "");