mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
PY-16063 Fixes according to review:
* Using ancestor types to prevent SO * Contexts are passed
This commit is contained in:
@@ -32,6 +32,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -153,8 +154,13 @@ public class PyJavaClassType implements PyClassLikeType {
|
||||
@Override
|
||||
public void visitMembers(@NotNull final Processor<PsiElement> processor, final boolean inherited, @NotNull TypeEvalContext context) {
|
||||
// TODO: Implement
|
||||
// We do not have enough time to this method for Java and looks like there is no need to do that since
|
||||
// jython is not very popular
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<PyClassLikeType> getAncestorTypes(@NotNull final TypeEvalContext context) {
|
||||
// TODO: Implement
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -74,7 +74,7 @@ public class PyJavaTypeProvider extends PyTypeProviderBase {
|
||||
final int index = params.indexOf(param);
|
||||
if (index < 0) return null;
|
||||
final List<PyType> superMethodParameterTypes = new ArrayList<PyType>();
|
||||
PySuperMethodsSearch.search(func, null).forEach(new Processor<PsiElement>() {
|
||||
PySuperMethodsSearch.search(func, context).forEach(new Processor<PsiElement>() {
|
||||
public boolean process(final PsiElement psiElement) {
|
||||
if (psiElement instanceof PsiMethod) {
|
||||
final PsiMethod method = (PsiMethod)psiElement;
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.util.Map;
|
||||
* Represents a class declaration in source.
|
||||
*/
|
||||
public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefiner, PyDocStringOwner, StubBasedPsiElement<PyClassStub>,
|
||||
ScopeOwner, PyDecoratable, PyTypedElement, PyQualifiedNameOwner, PyStatementListContainer {
|
||||
ScopeOwner, PyDecoratable, PyTypedElement, PyQualifiedNameOwner, PyStatementListContainer, PyWithAncestors {
|
||||
ArrayFactory<PyClass> ARRAY_FACTORY = new ArrayFactory<PyClass>() {
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -51,12 +51,6 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
ASTNode getNameNode();
|
||||
|
||||
|
||||
/**
|
||||
* Returns types of all ancestors from the hierarchy.
|
||||
*/
|
||||
@NotNull
|
||||
List<PyClassLikeType> getAncestorTypes(@NotNull TypeEvalContext context);
|
||||
|
||||
/**
|
||||
* Returns only those ancestors from the hierarchy, that are resolved to PyClass PSI elements.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.jetbrains.python.psi.types.PyClassLikeType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class or class-like entity with ancestors
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public interface PyWithAncestors {
|
||||
/**
|
||||
* Returns types of all ancestors from the hierarchy.
|
||||
*/
|
||||
@NotNull
|
||||
List<PyClassLikeType> getAncestorTypes(@NotNull TypeEvalContext context);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.Processor;
|
||||
import com.jetbrains.python.psi.AccessDirection;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PyWithAncestors;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.RatedResolveResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -29,7 +30,7 @@ import java.util.List;
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public interface PyClassLikeType extends PyCallableType {
|
||||
public interface PyClassLikeType extends PyCallableType, PyWithAncestors {
|
||||
boolean isDefinition();
|
||||
|
||||
PyClassLikeType toInstance();
|
||||
@@ -45,6 +46,7 @@ public interface PyClassLikeType extends PyCallableType {
|
||||
@NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext,
|
||||
boolean inherited);
|
||||
|
||||
// TODO: Pull to PyType at next iteration
|
||||
/**
|
||||
* Visits all class members. This method is better then bare class since it uses type info and supports not only classes but
|
||||
* class-like structures as well. Consider using user-friendly wrapper {@link PyClassLikeTypeUtil#getMembersOfType(PyClassLikeType, Class, TypeEvalContext)}
|
||||
|
||||
@@ -178,6 +178,17 @@ public class PyCustomType implements PyClassLikeType {
|
||||
return resolveMember(name, location, direction, resolveContext, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public final List<PyClassLikeType> getAncestorTypes(@NotNull final TypeEvalContext context) {
|
||||
final Collection<PyClassLikeType> result = new LinkedHashSet<PyClassLikeType>();
|
||||
for (final PyClassLikeType type : myTypesToMimic) {
|
||||
result.addAll(type.getAncestorTypes(context));
|
||||
}
|
||||
|
||||
return new ArrayList<PyClassLikeType>(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object[] getCompletionVariants(final String completionPrefix, final PsiElement location, final ProcessingContext context) {
|
||||
final Collection<Object> lookupElements = new ArrayList<Object>();
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.jetbrains.python.findUsages.PyFunctionFindUsagesHandler;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -128,7 +129,8 @@ public class PyStaticCallHierarchyUtil {
|
||||
@Nullable
|
||||
private static FindUsagesHandler createFindUsageHandler(@NotNull final PsiElement element) {
|
||||
if (element instanceof PyFunction) {
|
||||
final Collection<PsiElement> superMethods = PySuperMethodsSearch.search((PyFunction)element, true, null).findAll();
|
||||
final TypeEvalContext context = TypeEvalContext.userInitiated(element.getProject(), null);
|
||||
final Collection<PsiElement> superMethods = PySuperMethodsSearch.search((PyFunction)element, true, context).findAll();
|
||||
if (superMethods.size() > 0) {
|
||||
final PsiElement next = superMethods.iterator().next();
|
||||
if (next instanceof PyFunction && !isInObject((PyFunction)next)) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.refactoring.changeSignature.PyChangeSignatureDialog;
|
||||
import com.jetbrains.python.refactoring.changeSignature.PyMethodDescriptor;
|
||||
import com.jetbrains.python.refactoring.changeSignature.PyParameterInfo;
|
||||
@@ -58,7 +59,8 @@ public class PyChangeSignatureQuickFix implements LocalQuickFix {
|
||||
assert cls != null;
|
||||
final String functionName = function.getName();
|
||||
final String complementaryName = PyNames.NEW.equals(functionName) ? PyNames.INIT : PyNames.NEW;
|
||||
final PyFunction complementaryMethod = myOverridenMethod ? (PyFunction)PySuperMethodsSearch.search(function, null).findFirst()
|
||||
final TypeEvalContext context = TypeEvalContext.userInitiated(project, descriptor.getEndElement().getContainingFile());
|
||||
final PyFunction complementaryMethod = myOverridenMethod ? (PyFunction)PySuperMethodsSearch.search(function, context).findFirst()
|
||||
: cls.findMethodByName(complementaryName, true);
|
||||
|
||||
assert complementaryMethod != null;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class KeywordArgumentCompletionUtil {
|
||||
// nothing interesting besides self and **kwargs, let's look at superclass (PY-778)
|
||||
if (fromStatementCallCollector.isKwArgsTransit()) {
|
||||
|
||||
final PsiElement superMethod = PySuperMethodsSearch.search(def, null).findFirst();
|
||||
final PsiElement superMethod = PySuperMethodsSearch.search(def, context).findFirst();
|
||||
if (superMethod instanceof PyFunction) {
|
||||
addKeywordArgumentVariants((PyFunction)superMethod, callExpr, ret, visited);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory<PsiElement, PyS
|
||||
myContext = context;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TypeEvalContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
@@ -81,12 +82,12 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory<PsiElement, PyS
|
||||
super("Pythonid");
|
||||
}
|
||||
|
||||
public static Query<PsiElement> search(final PyFunction derivedMethod, TypeEvalContext context) {
|
||||
public static Query<PsiElement> search(final PyFunction derivedMethod, @Nullable final TypeEvalContext context) {
|
||||
final SearchParameters parameters = new SearchParameters(derivedMethod, false, context);
|
||||
return INSTANCE.createUniqueResultsQuery(parameters);
|
||||
}
|
||||
|
||||
public static Query<PsiElement> search(final PyFunction derivedMethod, boolean deepSearch, TypeEvalContext context) {
|
||||
public static Query<PsiElement> search(final PyFunction derivedMethod, final boolean deepSearch, @Nullable final TypeEvalContext context) {
|
||||
final SearchParameters parameters = new SearchParameters(derivedMethod, deepSearch, context);
|
||||
return INSTANCE.createUniqueResultsQuery(parameters);
|
||||
}
|
||||
|
||||
@@ -346,6 +346,12 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public final List<PyClassLikeType> getAncestorTypes(@NotNull final TypeEvalContext context) {
|
||||
return myClass.getAncestorTypes(context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveClassMember(@NotNull PyClass cls,
|
||||
boolean isDefinition,
|
||||
@@ -476,12 +482,14 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
|
||||
// TODO: accept instance attributes as well
|
||||
|
||||
if (inherited) {
|
||||
// TODO: Add guard to prevent stack overflow
|
||||
for (final PyClassLikeType type : getSuperClassTypes(context)) {
|
||||
if (type != null) {
|
||||
type.visitMembers(processor, true, context);
|
||||
}
|
||||
if (!inherited) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final PyClassLikeType type : getAncestorTypes(context)) {
|
||||
if (type != null) {
|
||||
// "false" because getAncestorTypes returns ALL ancestors, not only direct parents
|
||||
type.visitMembers(processor, false, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
7
python/testData/override/circle.py
Normal file
7
python/testData/override/circle.py
Normal file
@@ -0,0 +1,7 @@
|
||||
class Spam(Eggs):
|
||||
def spam_methods(self):
|
||||
pass
|
||||
|
||||
class Eggs(Spam):
|
||||
def my_methods(self):
|
||||
pass
|
||||
10
python/testData/override/circle_after.py
Normal file
10
python/testData/override/circle_after.py
Normal file
@@ -0,0 +1,10 @@
|
||||
class Spam(Eggs):
|
||||
def spam_methods(self):
|
||||
pass
|
||||
|
||||
class Eggs(Spam):
|
||||
def spam_methods(self):
|
||||
super(Eggs, self).spam_methods()
|
||||
|
||||
def my_methods(self):
|
||||
pass
|
||||
@@ -54,6 +54,13 @@ public class PyOverrideTest extends PyTestCase {
|
||||
return file.getTopLevelClasses().get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures loops in class hierarchy does not lead to SO
|
||||
*/
|
||||
public final void testCircle() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testSimple() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user