mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
committed by
Aleksei Kniazev
parent
362ed06439
commit
3a8db4d576
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user