PY-16063 Use type info instead of direct class access to support methods from class-like types.

This commit is contained in:
Ilya.Kazakevich
2015-09-02 23:31:25 +03:00
parent ee7eb7f964
commit f3b86e22c0
22 changed files with 255 additions and 60 deletions
@@ -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;
}
}
@@ -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;
}
}
}