mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-16063 Use type info instead of direct class access to support methods from class-like types.
This commit is contained in:
@@ -17,6 +17,7 @@ package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.Processor;
|
||||
import com.jetbrains.python.psi.AccessDirection;
|
||||
import com.jetbrains.python.psi.PyCallSiteExpression;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
@@ -149,6 +150,13 @@ public class PyJavaClassType implements PyClassLikeType {
|
||||
return result;
|
||||
}
|
||||
|
||||
@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
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return myClass.isValid();
|
||||
|
||||
@@ -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).forEach(new Processor<PsiElement>() {
|
||||
PySuperMethodsSearch.search(func, null).forEach(new Processor<PsiElement>() {
|
||||
public boolean process(final PsiElement psiElement) {
|
||||
if (psiElement instanceof PsiMethod) {
|
||||
final PsiMethod method = (PsiMethod)psiElement;
|
||||
|
||||
@@ -59,6 +59,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Returns only those ancestors from the hierarchy, that are resolved to PyClass PSI elements.
|
||||
*
|
||||
* @param context type eval context (pass null to use loose, but better provide one)
|
||||
* @see #getAncestorTypes(TypeEvalContext) for the full list of ancestors.
|
||||
*/
|
||||
@@ -67,7 +68,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Returns types of expressions in the super classes list.
|
||||
*
|
||||
* <p/>
|
||||
* If no super classes are specified, returns the type of the implicit super class for old- and new-style classes.
|
||||
*
|
||||
* @see #getAncestorTypes(TypeEvalContext) for the full list of ancestors.
|
||||
@@ -77,7 +78,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Returns only those super classes for expressions from the super classes list, that are resolved to PyClass PSI elements.
|
||||
*
|
||||
* <p/>
|
||||
* If no super classes are specified, returns the implicit super class for old- and new-style classes.
|
||||
*
|
||||
* @see #getSuperClassTypes(TypeEvalContext) for the full list of super classes.
|
||||
@@ -88,7 +89,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Returns a PSI element for the super classes list.
|
||||
*
|
||||
* <p/>
|
||||
* Operates at the AST level.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -96,7 +97,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Returns PSI elements for the expressions in the super classes list.
|
||||
*
|
||||
* <p/>
|
||||
* Operates at the AST level.
|
||||
*/
|
||||
@NotNull
|
||||
@@ -115,6 +116,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Get class properties.
|
||||
*
|
||||
* @return Map [property_name] = [{@link com.jetbrains.python.psi.Property}]
|
||||
*/
|
||||
@NotNull
|
||||
@@ -122,7 +124,8 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Finds a method with given name.
|
||||
* @param name what to look for
|
||||
*
|
||||
* @param name what to look for
|
||||
* @param inherited true: search in superclasses; false: only look for methods defined in this class.
|
||||
* @return
|
||||
*/
|
||||
@@ -134,8 +137,9 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
* If __init__ is defined, it is found first. This mimics the way initialization methods
|
||||
* are searched for and called by Python when a constructor call is made.
|
||||
* Since __new__ only makes sense for new-style classes, an old-style class never finds it with this method.
|
||||
*
|
||||
* @param inherited true: search in superclasses, too.
|
||||
* @param context TODO: DOC
|
||||
* @param context TODO: DOC
|
||||
* @return a method that would be called first when an instance of this class is instantiated.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -144,10 +148,9 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
/**
|
||||
* Finds a property with the specified name in the class or one of its ancestors.
|
||||
*
|
||||
*
|
||||
* @param name of the property
|
||||
* @param name of the property
|
||||
* @param inherited
|
||||
* @param context type eval (null to use loose context, but you better provide one)
|
||||
* @param context type eval (null to use loose context, but you better provide one)
|
||||
* @return descriptor of property accessors, or null if such property does not exist.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -155,11 +158,20 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Apply a processor to every method, looking at superclasses in method resolution order as needed.
|
||||
* Consider using {@link PyClassLikeType#visitMembers(Processor, boolean, TypeEvalContext)}
|
||||
*
|
||||
* @param processor what to apply
|
||||
* @param inherited true: search in superclasses, too.
|
||||
* @param context loose context will be used if no context provided
|
||||
* @see PyClassLikeType#visitMembers(Processor, boolean, TypeEvalContext)
|
||||
*/
|
||||
boolean visitMethods(Processor<PyFunction> processor, boolean inherited);
|
||||
boolean visitMethods(Processor<PyFunction> processor, boolean inherited, @Nullable TypeEvalContext context);
|
||||
|
||||
/**
|
||||
* Consider using {@link PyClassLikeType#visitMembers(Processor, boolean, TypeEvalContext)}
|
||||
*
|
||||
* @see PyClassLikeType#visitMembers(Processor, boolean, TypeEvalContext)
|
||||
*/
|
||||
boolean visitClassAttributes(Processor<PyTargetExpression> processor, boolean inherited, TypeEvalContext context);
|
||||
|
||||
/**
|
||||
@@ -168,6 +180,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
* This method does not access AST if underlying PSI is stub based.
|
||||
* Note that only <strong>own</strong> attrs are fetched, not parent attrs.
|
||||
* If you need parent attributes, consider using {@link #getClassAttributesInherited(TypeEvalContext)}
|
||||
*
|
||||
* @see #getClassAttributesInherited(TypeEvalContext)
|
||||
*/
|
||||
List<PyTargetExpression> getClassAttributes();
|
||||
@@ -176,6 +189,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
/**
|
||||
* Returns all class attributes this class class contains, including inherited one.
|
||||
* Process may be heavy, depending or your context.
|
||||
*
|
||||
* @param context context to use for this process
|
||||
* @return list of attrs.
|
||||
*/
|
||||
@@ -202,13 +216,14 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
PyClass findNestedClass(String name, boolean inherited);
|
||||
|
||||
/**
|
||||
* @return true if the class is new-style and descends from 'object'.
|
||||
* @param context
|
||||
* @return true if the class is new-style and descends from 'object'.
|
||||
*/
|
||||
boolean isNewStyleClass(TypeEvalContext context);
|
||||
|
||||
/**
|
||||
* Scan properties in order of definition, until processor returns true for one of them.
|
||||
*
|
||||
* @param processor to check properties
|
||||
* @param inherited whether inherited properties need to be scanned, too
|
||||
* @return a property that processor accepted, or null.
|
||||
@@ -235,6 +250,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Returns the aggregated list of names defined in __slots__ attributes of the class and its ancestors.
|
||||
*
|
||||
* @param context (will be used default if null)
|
||||
*/
|
||||
@Nullable
|
||||
@@ -253,12 +269,14 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
String getDocStringValue();
|
||||
|
||||
boolean processClassLevelDeclarations(@NotNull PsiScopeProcessor processor);
|
||||
|
||||
boolean processInstanceLevelDeclarations(@NotNull PsiScopeProcessor processor, @Nullable PsiElement location);
|
||||
|
||||
//TODO: Add "addMetaClass" or move methods out of here
|
||||
|
||||
/**
|
||||
* Returns the type representing the metaclass of the class if it is explicitly set, null otherwise.
|
||||
*
|
||||
* <p/>
|
||||
* The metaclass might be defined outside the class in case of Python 2 file-level __metaclass__ attributes.
|
||||
*/
|
||||
@Nullable
|
||||
@@ -266,14 +284,13 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
|
||||
|
||||
/**
|
||||
* Returns the expression that defines the metaclass of the class.
|
||||
*
|
||||
* <p/>
|
||||
* Operates at the AST level.
|
||||
*/
|
||||
@Nullable
|
||||
PyExpression getMetaClassExpression();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param context eval context
|
||||
* @return {@link com.jetbrains.python.psi.types.PyType} casted if it has right type
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
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.resolve.PyResolveContext;
|
||||
@@ -43,6 +45,17 @@ public interface PyClassLikeType extends PyCallableType {
|
||||
@NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext,
|
||||
boolean inherited);
|
||||
|
||||
/**
|
||||
* 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)}
|
||||
*
|
||||
* @param processor visitor
|
||||
* @param inherited call on parents too
|
||||
* @param context context to be used to resolve types
|
||||
* @see PyClassLikeTypeUtil#getMembersOfType(PyClassLikeType, Class, TypeEvalContext)
|
||||
*/
|
||||
void visitMembers(@NotNull Processor<PsiElement> processor, boolean inherited, @NotNull TypeEvalContext context);
|
||||
|
||||
boolean isValid();
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.types;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.Processor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tools and wrappers around {@link PyClassLikeType}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public final class PyClassLikeTypeUtil {
|
||||
private PyClassLikeTypeUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns members of certain type from {@link PyClassLikeType}
|
||||
*
|
||||
* @param type type itself
|
||||
* @param expectedMemberType expected member type
|
||||
* @param context context to use
|
||||
* @param <T> expected member type
|
||||
* @return collection of members
|
||||
*/
|
||||
@NotNull
|
||||
public static <T extends PsiElement> Collection<T> getMembersOfType(@NotNull final PyClassLikeType type,
|
||||
@NotNull final Class<T> expectedMemberType,
|
||||
@NotNull final TypeEvalContext context) {
|
||||
|
||||
final List<T> result = new ArrayList<T>();
|
||||
type.visitMembers(new Processor<PsiElement>() {
|
||||
@Override
|
||||
public boolean process(final PsiElement t) {
|
||||
if (expectedMemberType.isInstance(t)) {
|
||||
@SuppressWarnings("unchecked") // Already checked
|
||||
final T castedElement = (T)t;
|
||||
result.add(castedElement);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, true, context);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -241,6 +241,13 @@ public class PyCustomType implements PyClassLikeType {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMembers(@NotNull final Processor<PsiElement> processor, final boolean inherited, @NotNull TypeEvalContext context) {
|
||||
for (final PyClassLikeType type : myTypesToMimic) {
|
||||
type.visitMembers(processor, inherited, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate that filters completion using {@link #myFilter}
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.psi.impl.blockEvaluator.PyEvaluationContext;
|
||||
import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
|
||||
import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
|
||||
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
|
||||
@@ -111,7 +110,8 @@ public class PyLineMarkerProvider implements LineMarkerProvider, PyLineSeparator
|
||||
@Nullable
|
||||
protected Query<PsiElement> search(final PsiElement elt) {
|
||||
if (!(elt.getParent() instanceof PyFunction)) return null;
|
||||
return PySuperMethodsSearch.search((PyFunction)elt.getParent());
|
||||
final TypeEvalContext context = TypeEvalContext.codeAnalysis(elt.getProject(), null);
|
||||
return PySuperMethodsSearch.search((PyFunction)elt.getParent(), context);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,7 +180,8 @@ public class PyLineMarkerProvider implements LineMarkerProvider, PyLineSeparator
|
||||
if (PyNames.INIT.equals(function.getName())) {
|
||||
return null;
|
||||
}
|
||||
final PsiElement superMethod = PySuperMethodsSearch.search(function).findFirst();
|
||||
final TypeEvalContext context = TypeEvalContext.codeAnalysis(element.getProject(), null);
|
||||
final PsiElement superMethod = PySuperMethodsSearch.search(function, context).findFirst();
|
||||
if (superMethod != null) {
|
||||
PyClass superClass = null;
|
||||
if (superMethod instanceof PyFunction) {
|
||||
|
||||
@@ -40,8 +40,7 @@ import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyFunctionBuilder;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.types.PyNoneType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -73,25 +72,32 @@ public class PyOverrideImplementUtil {
|
||||
}
|
||||
final PyClass pyClass = PsiTreeUtil.getParentOfType(element, PyClass.class, false);
|
||||
if (pyClass == null && element instanceof PsiWhiteSpace && element.getPrevSibling() instanceof PyClass) {
|
||||
return (PyClass) element.getPrevSibling();
|
||||
return (PyClass)element.getPrevSibling();
|
||||
}
|
||||
return pyClass;
|
||||
}
|
||||
|
||||
public static void chooseAndOverrideMethods(final Project project, @NotNull final Editor editor, @NotNull final PyClass pyClass) {
|
||||
|
||||
|
||||
FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.CODEASSISTS_OVERRIDE_IMPLEMENT);
|
||||
chooseAndOverrideOrImplementMethods(project, editor, pyClass);
|
||||
}
|
||||
|
||||
|
||||
private static void chooseAndOverrideOrImplementMethods(final Project project,
|
||||
@NotNull final Editor editor,
|
||||
@NotNull final PyClass pyClass) {
|
||||
@NotNull final Editor editor,
|
||||
@NotNull final PyClass pyClass) {
|
||||
LOG.assertTrue(pyClass.isValid());
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
|
||||
final Collection<PyFunction> superFunctions = getAllSuperFunctions(pyClass);
|
||||
chooseAndOverrideOrImplementMethods(project, editor, pyClass, superFunctions, "Select Methods to Override", false);
|
||||
final Set<PyFunction> result = new HashSet<PyFunction>();
|
||||
TypeEvalContext context = TypeEvalContext.codeCompletion(project, null);
|
||||
final Collection<PyFunction> superFunctions = getAllSuperFunctions(pyClass, context);
|
||||
|
||||
|
||||
result.addAll(superFunctions);
|
||||
chooseAndOverrideOrImplementMethods(project, editor, pyClass, result, "Select Methods to Override", false);
|
||||
}
|
||||
|
||||
public static void chooseAndOverrideOrImplementMethods(@NotNull final Project project,
|
||||
@@ -155,16 +161,17 @@ public class PyOverrideImplementUtil {
|
||||
final PyStatementList statementList = pyClass.getStatementList();
|
||||
final int offset = editor.getCaretModel().getOffset();
|
||||
PsiElement anchor = null;
|
||||
for (PyStatement statement: statementList.getStatements()) {
|
||||
for (PyStatement statement : statementList.getStatements()) {
|
||||
if (statement.getTextRange().getStartOffset() < offset ||
|
||||
(statement instanceof PyExpressionStatement && ((PyExpressionStatement)statement).getExpression() instanceof PyStringLiteralExpression)) {
|
||||
(statement instanceof PyExpressionStatement &&
|
||||
((PyExpressionStatement)statement).getExpression() instanceof PyStringLiteralExpression)) {
|
||||
anchor = statement;
|
||||
}
|
||||
}
|
||||
|
||||
PyFunction element = null;
|
||||
for (PyMethodMember newMember : newMembers) {
|
||||
PyFunction baseFunction = (PyFunction) newMember.getPsiElement();
|
||||
PyFunction baseFunction = (PyFunction)newMember.getPsiElement();
|
||||
final PyFunctionBuilder builder = buildOverriddenFunction(pyClass, baseFunction, implement);
|
||||
PyFunction function = builder.addFunctionAfter(statementList, anchor, LanguageLevel.forElement(statementList));
|
||||
element = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(function);
|
||||
@@ -210,7 +217,7 @@ public class PyOverrideImplementUtil {
|
||||
|
||||
boolean hadStar = false;
|
||||
List<String> parameters = new ArrayList<String>();
|
||||
for (PyParameter parameter: baseParams) {
|
||||
for (PyParameter parameter : baseParams) {
|
||||
final PyNamedParameter pyNamedParameter = parameter.getAsNamed();
|
||||
if (pyNamedParameter != null) {
|
||||
String repr = pyNamedParameter.getRepr(false);
|
||||
@@ -244,7 +251,7 @@ public class PyOverrideImplementUtil {
|
||||
PsiElement outerClass = PsiTreeUtil.getParentOfType(pyClass, PyClass.class, true, PyFunction.class);
|
||||
String className = pyClass.getName();
|
||||
final List<String> nameResult = Lists.newArrayList(className);
|
||||
while(outerClass != null) {
|
||||
while (outerClass != null) {
|
||||
nameResult.add(0, ((PyClass)outerClass).getName());
|
||||
outerClass = PsiTreeUtil.getParentOfType(outerClass, PyClass.class, true, PyFunction.class);
|
||||
}
|
||||
@@ -297,7 +304,7 @@ public class PyOverrideImplementUtil {
|
||||
final PyExpression[] superClassExpressions = fromClass.getSuperClassExpressions();
|
||||
for (PyExpression expression : superClassExpressions) {
|
||||
if (expression instanceof PyReferenceExpression) {
|
||||
PsiElement target = ((PyReferenceExpression) expression).getReference().resolve();
|
||||
PsiElement target = ((PyReferenceExpression)expression).getReference().resolve();
|
||||
if (target == toClass) {
|
||||
return expression.getText();
|
||||
}
|
||||
@@ -307,13 +314,24 @@ public class PyOverrideImplementUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Collection<PyFunction> getAllSuperFunctions(@NotNull PyClass pyClass) {
|
||||
public static Collection<PyFunction> getAllSuperFunctions(@NotNull PyClass pyClass, @NotNull TypeEvalContext context) {
|
||||
|
||||
// This is a legacy approach. Should be removed soon since type-based members should be enough
|
||||
final Map<String, PyFunction> superFunctions = new HashMap<String, PyFunction>();
|
||||
for (PyFunction function : pyClass.getMethods(true)) {
|
||||
if (!superFunctions.containsKey(function.getName())) {
|
||||
superFunctions.put(function.getName(), function);
|
||||
}
|
||||
}
|
||||
return superFunctions.values();
|
||||
|
||||
|
||||
|
||||
final Set<PyFunction> functions = new HashSet<PyFunction>(superFunctions.values());
|
||||
final PyClassLikeType type = PyUtil.as(context.getType(pyClass), PyClassLikeType.class);
|
||||
|
||||
if (type != null) {
|
||||
functions.addAll(PyClassLikeTypeUtil.getMembersOfType(type, PyFunction.class, context));
|
||||
}
|
||||
return functions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class CreateTestAction extends PsiElementBaseIntentionAction {
|
||||
methods.add(pyFunction);
|
||||
return true;
|
||||
}
|
||||
}, false);
|
||||
}, false, null);
|
||||
|
||||
d.methodsSize(methods.size());
|
||||
int i = 0;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class PyFindUsagesHandlerFactory extends FindUsagesHandlerFactory {
|
||||
}
|
||||
if (element instanceof PyFunction) {
|
||||
if (!forHighlightUsages) {
|
||||
final Collection<PsiElement> superMethods = PySuperMethodsSearch.search((PyFunction)element, true).findAll();
|
||||
final Collection<PsiElement> superMethods = PySuperMethodsSearch.search((PyFunction)element, true, null).findAll();
|
||||
if (superMethods.size() > 0) {
|
||||
final PsiElement next = superMethods.iterator().next();
|
||||
// TODO should do this for Jython functions overriding Java methods too
|
||||
|
||||
@@ -128,7 +128,7 @@ 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).findAll();
|
||||
final Collection<PsiElement> superMethods = PySuperMethodsSearch.search((PyFunction)element, true, null).findAll();
|
||||
if (superMethods.size() > 0) {
|
||||
final PsiElement next = superMethods.iterator().next();
|
||||
if (next instanceof PyFunction && !isInObject((PyFunction)next)) {
|
||||
|
||||
@@ -66,7 +66,7 @@ public class PyAbstractClassInspection extends PyInspection {
|
||||
return;
|
||||
}
|
||||
final Set<PyFunction> toBeImplemented = new HashSet<PyFunction>();
|
||||
final Collection<PyFunction> functions = PyOverrideImplementUtil.getAllSuperFunctions(pyClass);
|
||||
final Collection<PyFunction> functions = PyOverrideImplementUtil.getAllSuperFunctions(pyClass, myTypeEvalContext);
|
||||
for (PyFunction method : functions) {
|
||||
if (isAbstractMethodForClass(method, pyClass)) {
|
||||
toBeImplemented.add(method);
|
||||
|
||||
@@ -64,7 +64,7 @@ public class PyMethodMayBeStaticInspection extends PyInspection {
|
||||
final PyClass containingClass = node.getContainingClass();
|
||||
if (containingClass == null) return;
|
||||
if (PythonUnitTestUtil.isUnitTestCaseClass(containingClass)) return;
|
||||
final PsiElement firstSuper = PySuperMethodsSearch.search(node).findFirst();
|
||||
final PsiElement firstSuper = PySuperMethodsSearch.search(node, null).findFirst();
|
||||
if (firstSuper != null) return;
|
||||
final PyFunction firstOverride = PyOverridingMethodsSearch.search(node, true).findFirst();
|
||||
if (firstOverride != null) return;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class PyMethodOverridingInspection extends PyInspection {
|
||||
String name = function.getName();
|
||||
if (PyNames.INIT.equals(name) || PyNames.NEW.equals(name)) return; // these are expected to change signature
|
||||
// real work
|
||||
for (PsiElement psiElement : PySuperMethodsSearch.search(function)) {
|
||||
for (PsiElement psiElement : PySuperMethodsSearch.search(function, null)) {
|
||||
if (psiElement instanceof PyFunction) {
|
||||
final PyFunction baseMethod = (PyFunction)psiElement;
|
||||
final PyClass baseClass = baseMethod.getContainingClass();
|
||||
|
||||
@@ -138,7 +138,7 @@ public class PyPep8NamingInspection extends PyInspection {
|
||||
}
|
||||
|
||||
private boolean isOverriddenMethod(@NotNull PyFunction function) {
|
||||
return PySuperMethodsSearch.search(function).findFirst() != null;
|
||||
return PySuperMethodsSearch.search(function, null).findFirst() != null;
|
||||
}
|
||||
|
||||
private boolean isIgnoredOrHasIgnoredAncestor(@NotNull PyClass pyClass) {
|
||||
|
||||
@@ -353,7 +353,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor {
|
||||
if (functionsWithInheritors.contains(func)) {
|
||||
return true;
|
||||
}
|
||||
if (PySuperMethodsSearch.search(func).findFirst() != null ||
|
||||
if (PySuperMethodsSearch.search(func, null).findFirst() != null ||
|
||||
PyOverridingMethodsSearch.search(func, true).findFirst() != null) {
|
||||
functionsWithInheritors.add(func);
|
||||
return true;
|
||||
|
||||
@@ -58,7 +58,7 @@ 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).findFirst()
|
||||
final PyFunction complementaryMethod = myOverridenMethod ? (PyFunction)PySuperMethodsSearch.search(function, null).findFirst()
|
||||
: cls.findMethodByName(complementaryName, true);
|
||||
|
||||
assert complementaryMethod != null;
|
||||
|
||||
@@ -552,7 +552,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
public PyFunction findMethodByName(@Nullable final String name, boolean inherited) {
|
||||
if (name == null) return null;
|
||||
NameFinder<PyFunction> proc = new NameFinder<PyFunction>(name);
|
||||
visitMethods(proc, inherited);
|
||||
visitMethods(proc, inherited, null);
|
||||
return proc.getResult();
|
||||
}
|
||||
|
||||
@@ -920,11 +920,11 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
}
|
||||
}
|
||||
|
||||
public boolean visitMethods(Processor<PyFunction> processor, boolean inherited) {
|
||||
return visitMethods(processor, inherited, false, null);
|
||||
public boolean visitMethods(Processor<PyFunction> processor, boolean inherited, @Nullable final TypeEvalContext context) {
|
||||
return visitMethods(processor, inherited, false, context);
|
||||
}
|
||||
|
||||
public boolean visitMethods(Processor<PyFunction> processor,
|
||||
private boolean visitMethods(Processor<PyFunction> processor,
|
||||
boolean inherited,
|
||||
boolean skipClassObj, TypeEvalContext context) {
|
||||
PyFunction[] methods = getMethods(false);
|
||||
@@ -934,7 +934,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
if (skipClassObj && PyNames.FAKE_OLD_BASE.equals(ancestor.getName())) {
|
||||
continue;
|
||||
}
|
||||
if (!ancestor.visitMethods(processor, false)) {
|
||||
if (!ancestor.visitMethods(processor, false, null)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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).findFirst();
|
||||
final PsiElement superMethod = PySuperMethodsSearch.search(def, null).findFirst();
|
||||
if (superMethod instanceof PyFunction) {
|
||||
addKeywordArgumentVariants((PyFunction)superMethod, callExpr, ret, visited);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import com.intellij.psi.search.searches.ExtensibleQueryFactory;
|
||||
import com.intellij.util.Query;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -43,10 +45,10 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory<PsiElement, PyS
|
||||
}
|
||||
|
||||
public static PyFunction findDeepestSuperMethod(PyFunction function) {
|
||||
List<PsiElement> superMethods = new ArrayList<PsiElement>(search(function, true).findAll());
|
||||
List<PsiElement> superMethods = new ArrayList<PsiElement>(search(function, true, null).findAll());
|
||||
while (superMethods.size() > 0) {
|
||||
function = getBaseMethod(superMethods, function.getContainingClass());
|
||||
superMethods = new ArrayList<PsiElement>(search(function, true).findAll());
|
||||
superMethods = new ArrayList<PsiElement>(search(function, true, null).findAll());
|
||||
}
|
||||
return function;
|
||||
}
|
||||
@@ -54,10 +56,16 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory<PsiElement, PyS
|
||||
public static class SearchParameters {
|
||||
private final PyFunction myDerivedMethod;
|
||||
private final boolean myDeepSearch;
|
||||
private final TypeEvalContext myContext;
|
||||
|
||||
public SearchParameters(final PyFunction derivedMethod, boolean deepSearch) {
|
||||
public SearchParameters(final PyFunction derivedMethod, boolean deepSearch, @Nullable final TypeEvalContext context) {
|
||||
myDerivedMethod = derivedMethod;
|
||||
myDeepSearch = deepSearch;
|
||||
myContext = context;
|
||||
}
|
||||
|
||||
public TypeEvalContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
||||
public PyFunction getDerivedMethod() {
|
||||
@@ -73,13 +81,13 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory<PsiElement, PyS
|
||||
super("Pythonid");
|
||||
}
|
||||
|
||||
public static Query<PsiElement> search(final PyFunction derivedMethod) {
|
||||
final SearchParameters parameters = new SearchParameters(derivedMethod, false);
|
||||
public static Query<PsiElement> search(final PyFunction derivedMethod, TypeEvalContext context) {
|
||||
final SearchParameters parameters = new SearchParameters(derivedMethod, false, context);
|
||||
return INSTANCE.createUniqueResultsQuery(parameters);
|
||||
}
|
||||
|
||||
public static Query<PsiElement> search(final PyFunction derivedMethod, boolean deepSearch) {
|
||||
final SearchParameters parameters = new SearchParameters(derivedMethod, deepSearch);
|
||||
public static Query<PsiElement> search(final PyFunction derivedMethod, boolean deepSearch, TypeEvalContext context) {
|
||||
final SearchParameters parameters = new SearchParameters(derivedMethod, deepSearch, context);
|
||||
return INSTANCE.createUniqueResultsQuery(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.QueryExecutor;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.types.PyClassLikeType;
|
||||
import com.jetbrains.python.psi.types.PyClassLikeTypeUtil;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -28,7 +31,8 @@ import java.util.Set;
|
||||
* @author yole
|
||||
*/
|
||||
public class PySuperMethodsSearchExecutor implements QueryExecutor<PsiElement, PySuperMethodsSearch.SearchParameters> {
|
||||
public boolean execute(@NotNull final PySuperMethodsSearch.SearchParameters queryParameters, @NotNull final Processor<PsiElement> consumer) {
|
||||
public boolean execute(@NotNull final PySuperMethodsSearch.SearchParameters queryParameters,
|
||||
@NotNull final Processor<PsiElement> consumer) {
|
||||
PyFunction func = queryParameters.getDerivedMethod();
|
||||
String name = func.getName();
|
||||
PyClass containingClass = func.getContainingClass();
|
||||
@@ -56,6 +60,21 @@ public class PySuperMethodsSearchExecutor implements QueryExecutor<PsiElement, P
|
||||
superMethod = (callable instanceof PyFunction) ? (PyFunction)callable : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final TypeEvalContext context = queryParameters.getContext();
|
||||
if (superMethod == null && context != null) {
|
||||
// If super method still not found and we have context, we may use it to find method
|
||||
final PyClassLikeType classLikeType = PyUtil.as(context.getType(superClass), PyClassLikeType.class);
|
||||
if (classLikeType != null) {
|
||||
for (final PyFunction function : PyClassLikeTypeUtil.getMembersOfType(classLikeType, PyFunction.class, context)) {
|
||||
final String elemName = function.getName();
|
||||
if (elemName != null && elemName.equals(queryParameters.getDerivedMethod().getName())) {
|
||||
consumer.process(function);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (superMethod != null) {
|
||||
foundMethodContainingClasses.add(superClass);
|
||||
if (!consumer.process(superMethod)) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.PyCustomMember;
|
||||
@@ -233,7 +234,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
|
||||
if (inherited) {
|
||||
classMember =
|
||||
resolveByMembersProviders(this, name, location, context); //ask providers after real class introspection as providers have less priority
|
||||
resolveByMembersProviders(this, name, location,
|
||||
context); //ask providers after real class introspection as providers have less priority
|
||||
}
|
||||
|
||||
if (classMember != null) {
|
||||
@@ -244,7 +246,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
for (PyClassLikeType type : myClass.getAncestorTypes(context)) {
|
||||
if (type instanceof PyClassType) {
|
||||
final PyClass pyClass = ((PyClassType)type).getPyClass();
|
||||
PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location, resolveContext.getTypeEvalContext());
|
||||
PsiElement superMember =
|
||||
resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location, resolveContext.getTypeEvalContext());
|
||||
|
||||
if (superMember != null) {
|
||||
return ResolveResultList.to(superMember);
|
||||
@@ -256,7 +259,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private Ref<ResolveResultList> findProperty(String name, AccessDirection direction, boolean inherited, @Nullable TypeEvalContext context) {
|
||||
private Ref<ResolveResultList> findProperty(String name,
|
||||
AccessDirection direction,
|
||||
boolean inherited,
|
||||
@Nullable TypeEvalContext context) {
|
||||
Ref<ResolveResultList> resultRef = null;
|
||||
Property property = myClass.findProperty(name, inherited, context);
|
||||
if (property != null) {
|
||||
@@ -460,6 +466,26 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
return ret.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMembers(@NotNull final Processor<PsiElement> processor,
|
||||
final boolean inherited,
|
||||
@NotNull final TypeEvalContext context) {
|
||||
|
||||
myClass.visitMethods(new MyProcessorWrapper<PyFunction>(processor), false, context);
|
||||
myClass.visitClassAttributes(new MyProcessorWrapper<PyTargetExpression>(processor), false, context);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addOwnClassMembers(PsiElement expressionHook, Set<String> namesAlready, boolean suppressParentheses, List<Object> ret) {
|
||||
PyClass containingClass = PsiTreeUtil.getParentOfType(expressionHook, PyClass.class);
|
||||
if (containingClass != null) {
|
||||
@@ -476,7 +502,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
myClass.processClassLevelDeclarations(processor);
|
||||
|
||||
// We are here because of completion (see call stack), so we use code complete here
|
||||
final TypeEvalContext context = (expressionHook != null ? TypeEvalContext.codeCompletion(myClass.getProject(), myClass.getContainingFile()) :null);
|
||||
final TypeEvalContext context =
|
||||
(expressionHook != null ? TypeEvalContext.codeCompletion(myClass.getProject(), myClass.getContainingFile()) : null);
|
||||
List<String> slots = myClass.isNewStyleClass(null) ? myClass.getSlots(
|
||||
context) : null;
|
||||
if (slots != null) {
|
||||
@@ -621,4 +648,18 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
}
|
||||
return new PyClassTypeImpl(pyClass, isDefinition);
|
||||
}
|
||||
|
||||
private static final class MyProcessorWrapper<T extends PsiElement> implements Processor<T> {
|
||||
private final Processor<PsiElement> myProcessor;
|
||||
|
||||
private MyProcessorWrapper(@NotNull final Processor<PsiElement> processor) {
|
||||
myProcessor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(final T t) {
|
||||
myProcessor.process(t);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user