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 9be910772f54..5eac0b1a067d 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -3,6 +3,7 @@ package com.jetbrains.python.psi.impl.references; import com.google.common.collect.Lists; import com.intellij.codeInsight.completion.CompletionUtil; +import com.intellij.codeInsight.controlflow.ControlFlowUtil; import com.intellij.codeInsight.controlflow.Instruction; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; @@ -10,6 +11,7 @@ import com.intellij.lang.ASTNode; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; @@ -692,6 +694,30 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return false; } } + } else if (ScopeUtil.getScopeOwner(e) == ScopeUtil.getScopeOwner(element)) { + String name = null; + ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(e); + if (e instanceof PsiNamedElement) { + name = ((PsiNamedElement) e).getName(); + } else if (e instanceof PyImportElement) { + name = ((PyImportElement) e).getName(); + } else if (e instanceof PyImportedNameDefiner && scopeOwner != null) { + Ref importFound = Ref.create(false); + Instruction[] instructions = ControlFlowCache.getControlFlow(scopeOwner).getInstructions(); + int completionIndex = ControlFlowUtil.findInstructionNumberByElement(instructions, element); + ControlFlowUtil.iteratePrev(completionIndex, instructions, instruction -> { + if (instruction.getElement() == e) { + importFound.set(true); + return ControlFlowUtil.Operation.BREAK; + } + return ControlFlowUtil.Operation.NEXT; + }); + return importFound.get(); + } + if (name != null && scopeOwner != null) { + List defs = getLatestDefinitions(name, scopeOwner, element); + return !defs.isEmpty(); + } } return true; }, null); diff --git a/python/testData/completion/beforeImport/beforeImport.py b/python/testData/completion/beforeImport/beforeImport.py new file mode 100644 index 000000000000..64d99341098d --- /dev/null +++ b/python/testData/completion/beforeImport/beforeImport.py @@ -0,0 +1,3 @@ +m + +from source import my_foo diff --git a/python/testData/completion/beforeImport/beforeImportAs.py b/python/testData/completion/beforeImport/beforeImportAs.py new file mode 100644 index 000000000000..6580f66c87dc --- /dev/null +++ b/python/testData/completion/beforeImport/beforeImportAs.py @@ -0,0 +1,3 @@ +m + +from source import my_foo as my_renamed_foo diff --git a/python/testData/completion/beforeImport/beforeStarImport.py b/python/testData/completion/beforeImport/beforeStarImport.py new file mode 100644 index 000000000000..f077e8e6c4e0 --- /dev/null +++ b/python/testData/completion/beforeImport/beforeStarImport.py @@ -0,0 +1,3 @@ +my_ + +from source import * diff --git a/python/testData/completion/beforeImport/source.py b/python/testData/completion/beforeImport/source.py new file mode 100644 index 000000000000..51c54af584ad --- /dev/null +++ b/python/testData/completion/beforeImport/source.py @@ -0,0 +1,8 @@ + + +def my_foo(): + pass + + +def my_bar(): + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 51ab0d4e57be..90c698d793d9 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -1425,6 +1425,80 @@ public class PythonCompletionTest extends PyTestCase { assertContainsElements(suggested, "'k1'", "'k2'"); } + // PY-8302 + public void testUndeclaredFunction() { + List suggested = doTestByText("f\n" + + "\n" + + "def foo(yyy):\n" + + " y = 1"); + assertNotNull(suggested); + assertDoesntContain(suggested, "foo"); + } + + // PY-8302 + public void testUninitializedVarBefore() { + List suggested = doTestByText("f\n" + + "\n" + + "foo = \"something\"\n"); + assertNotNull(suggested); + assertDoesntContain(suggested, "foo"); + } + + // PY-8302 + public void testUninitializedVarOnSameLine() { + List suggested = doTestByText("foo = f"); + assertNotNull(suggested); + assertDoesntContain(suggested, "foo"); + } + + // PY-8302 + public void testUninitializedVarOnMultiLine() { + List suggested = doTestByText("foo = \"this is a string\"\\\n" + + " \"on several lines\" + f"); + assertNotNull(suggested); + assertDoesntContain(suggested, "foo"); + } + + // PY-8302 + public void testUndeclaredClass() { + List suggested = doTestByText("class AClass(A)"); + assertNotNull(suggested); + assertDoesntContain(suggested, "AClass"); + } + + // PY-8302 + public void testDeclaredClass() { + List suggested = doTestByText("class AClass:\n" + + " pass\n\n" + + "class BClass(A)"); + assertNotNull(suggested); + assertContainsElements(suggested, "AClass"); + } + + // PY-8302 + public void testBeforeImport() { + myFixture.configureByFiles("beforeImport/beforeImport.py", "beforeImport/source.py"); + myFixture.completeBasic(); + List suggested = myFixture.getLookupElementStrings(); + assertDoesntContain(suggested, "my_foo", "my_bar"); + } + + // PY-8302 + public void testBeforeImportAs() { + myFixture.configureByFiles("beforeImport/beforeImportAs.py", "beforeImport/source.py"); + myFixture.completeBasic(); + List suggested = myFixture.getLookupElementStrings(); + assertDoesntContain(suggested, "my_renamed_foo"); + } + + // PY-8302 + public void testBeforeStarImport() { + myFixture.configureByFiles("beforeImport/beforeStarImport.py", "beforeImport/source.py"); + myFixture.completeBasic(); + List suggested = myFixture.getLookupElementStrings(); + assertDoesntContain(suggested, "my_foo", "my_bar"); + } + private void assertNoVariantsInExtendedCompletion() { myFixture.copyDirectoryToProject(getTestName(true), ""); myFixture.configureByFile("a.py");