Fix completion private members described in stubs (PY-27146)

No more private element filtering in CompletionVariantsProcessor, let's leave it for types.
This commit is contained in:
Semyon Proshev
2017-12-04 13:51:09 +03:00
parent 3293db67bf
commit 60d2063713
9 changed files with 52 additions and 20 deletions
@@ -157,13 +157,7 @@ public class PyProtectedMemberInspection extends PyInspection {
@Nullable
private PyClass getNotPyiClassOwner(@Nullable PsiElement element) {
final PyClass owner = getClassOwner(element);
if (owner != null) {
final PsiElement originalOwner = PyiUtil.getOriginalElement(owner);
if (originalOwner instanceof PyClass) {
return (PyClass)originalOwner;
}
}
return owner;
return owner == null ? null : PyiUtil.stubToOriginal(owner, PyClass.class);
}
@Nullable
@@ -128,10 +128,9 @@ public class PyQualifiedReference extends PyReferenceImpl {
return true;
}
if (match.size() > 1) {
final PyClass ourClass = qualifierType.getPyClass();
final PsiElement originalOurClass = PyiUtil.getOriginalElement(ourClass);
final PyClass ourClass = PyiUtil.stubToOriginal(qualifierType.getPyClass(), PyClass.class);
final PsiElement theirClass = CompletionUtil.getOriginalOrSelf(match.get(match.size() - 1));
if (originalOurClass != null && originalOurClass != theirClass || originalOurClass == null && ourClass != theirClass) return true;
if (ourClass != theirClass) return true;
}
return false;
}
@@ -156,9 +156,6 @@ public class CompletionVariantsProcessor extends VariantsProcessor {
@Override
protected void addElement(@NotNull String name, @NotNull PsiElement element) {
if (PyUtil.isClassPrivateName(name) && !PyUtil.inSameFile(element, myContext)) {
return;
}
markAsProcessed(name);
myVariants.put(name, setupItem(LookupElementBuilder.createWithSmartPointer(name, element).withIcon(element.getIcon(0))));
}
@@ -30,6 +30,7 @@ import com.jetbrains.python.psi.resolve.CompletionVariantsProcessor;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.PyResolveProcessor;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.pyi.PyiUtil;
import com.jetbrains.python.toolbox.Maybe;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
@@ -788,7 +789,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
if (containingClass != null) {
containingClass = CompletionUtil.getOriginalElement(containingClass);
}
boolean withinOurClass = containingClass == getPyClass() || isInSuperCall(expressionHook);
final boolean withinOurClass = containingClass == PyiUtil.stubToOriginal(getPyClass(), PyClass.class) || isInSuperCall(expressionHook);
final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(
expressionHook, new FilterNotInstance(myClass), null, false, suppressParentheses
@@ -808,7 +809,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
for (LookupElement le : processor.getResultList()) {
String name = le.getLookupString();
if (namesAlready.contains(name)) continue;
if (!withinOurClass && isClassPrivate(name)) continue;
if (!withinOurClass && PyUtil.isClassPrivateName(name)) continue;
if (!withinOurClass && isClassProtected(name) && prefix == null) continue;
namesAlready.add(name);
ret.add(le);
@@ -849,7 +850,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
Object[] ancestry = type.getCompletionVariants(name, expressionHook, context);
for (Object ob : ancestry) {
String inheritedName = ob.toString();
if (!namesAlready.contains(inheritedName) && !isClassPrivate(inheritedName)) {
if (!namesAlready.contains(inheritedName) && !PyUtil.isClassPrivateName(inheritedName)) {
ret.add(ob);
namesAlready.add(inheritedName);
}
@@ -858,10 +859,6 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
}
}
private static boolean isClassPrivate(String lookup_string) {
return lookup_string.startsWith("__") && !lookup_string.endsWith("__");
}
private static boolean isClassProtected(@NotNull final String lookupString) {
return lookupString.startsWith("_") && !lookupString.startsWith("__");
}
@@ -125,6 +125,15 @@ public class PyiUtil {
PyKnownDecoratorUtil.getKnownDecorators((PyFunction)element, context).contains(overload);
}
@NotNull
public static <T extends PyElement> T stubToOriginal(@NotNull T element, @NotNull Class<T> cls) {
final PsiElement originalElement = getOriginalElement(element);
if (cls.isInstance(originalElement)) {
return cls.cast(originalElement);
}
return element;
}
private static boolean pyButNotPyiFile(@Nullable PsiFile file) {
return file instanceof PyFile && !(file instanceof PyiFile);
}
@@ -0,0 +1,12 @@
class Test:
@classmethod
def foo(cls):
return cls._bar(), cls.__egg()
@staticmethod
def _bar():
pass
@staticmethod
def __egg():
pass
@@ -0,0 +1,12 @@
class Test:
@classmethod
def foo(cls):
return cls._bar(), cls.__eg<caret>
@staticmethod
def _bar():
pass
@staticmethod
def __egg():
pass
@@ -0,0 +1,7 @@
class Test:
@classmethod
def foo(cls): ...
@staticmethod
def _bar(): ...
@staticmethod
def __egg(): ...
@@ -1194,6 +1194,11 @@ public class PythonCompletionTest extends PyTestCase {
assertDoesntContain(variants, "bar=");
}
// PY-27146
public void testPrivateMemberOwnerResolvedToStub() {
doMultiFileTest();
}
@Override
protected String getTestDataPath() {
return super.getTestDataPath() + "/completion";