IDEA-CR-42930: check that function/variable/import has been initialized before suggesting it for autocomplete

(cherry picked from commit aa11f609df36b2b1b3ce6079f17082bd2b6df9f7)
This commit is contained in:
aleksei.kniazev
2019-03-21 14:34:54 +03:00
committed by Aleksei Kniazev
parent 362ed06439
commit 3a8db4d576
6 changed files with 117 additions and 0 deletions
@@ -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<Boolean> 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<Instruction> defs = getLatestDefinitions(name, scopeOwner, element);
return !defs.isEmpty();
}
}
return true;
}, null);
@@ -0,0 +1,3 @@
m<caret>
from source import my_foo
@@ -0,0 +1,3 @@
m<caret>
from source import my_foo as my_renamed_foo
@@ -0,0 +1,3 @@
my_<caret>
from source import *
@@ -0,0 +1,8 @@
def my_foo():
pass
def my_bar():
pass
@@ -1425,6 +1425,80 @@ public class PythonCompletionTest extends PyTestCase {
assertContainsElements(suggested, "'k1'", "'k2'");
}
// PY-8302
public void testUndeclaredFunction() {
List<String> suggested = doTestByText("f<caret>\n" +
"\n" +
"def foo(yyy):\n" +
" y = 1");
assertNotNull(suggested);
assertDoesntContain(suggested, "foo");
}
// PY-8302
public void testUninitializedVarBefore() {
List<String> suggested = doTestByText("f<caret>\n" +
"\n" +
"foo = \"something\"\n");
assertNotNull(suggested);
assertDoesntContain(suggested, "foo");
}
// PY-8302
public void testUninitializedVarOnSameLine() {
List<String> suggested = doTestByText("foo = f<caret>");
assertNotNull(suggested);
assertDoesntContain(suggested, "foo");
}
// PY-8302
public void testUninitializedVarOnMultiLine() {
List<String> suggested = doTestByText("foo = \"this is a string\"\\\n" +
" \"on several lines\" + f<caret>");
assertNotNull(suggested);
assertDoesntContain(suggested, "foo");
}
// PY-8302
public void testUndeclaredClass() {
List<String> suggested = doTestByText("class AClass(A<caret>)");
assertNotNull(suggested);
assertDoesntContain(suggested, "AClass");
}
// PY-8302
public void testDeclaredClass() {
List<String> suggested = doTestByText("class AClass:\n" +
" pass\n\n" +
"class BClass(A<caret>)");
assertNotNull(suggested);
assertContainsElements(suggested, "AClass");
}
// PY-8302
public void testBeforeImport() {
myFixture.configureByFiles("beforeImport/beforeImport.py", "beforeImport/source.py");
myFixture.completeBasic();
List<String> 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<String> suggested = myFixture.getLookupElementStrings();
assertDoesntContain(suggested, "my_renamed_foo");
}
// PY-8302
public void testBeforeStarImport() {
myFixture.configureByFiles("beforeImport/beforeStarImport.py", "beforeImport/source.py");
myFixture.completeBasic();
List<String> suggested = myFixture.getLookupElementStrings();
assertDoesntContain(suggested, "my_foo", "my_bar");
}
private void assertNoVariantsInExtendedCompletion() {
myFixture.copyDirectoryToProject(getTestName(true), "");
myFixture.configureByFile("a.py");