diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java index bd0f820b34fd..a3e88a2cbb2a 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaClassType.java @@ -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 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(); diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java index 50b16502cb9a..92ff19ee9662 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java @@ -74,7 +74,7 @@ public class PyJavaTypeProvider extends PyTypeProviderBase { final int index = params.indexOf(param); if (index < 0) return null; final List superMethodParameterTypes = new ArrayList(); - PySuperMethodsSearch.search(func).forEach(new Processor() { + PySuperMethodsSearch.search(func, null).forEach(new Processor() { public boolean process(final PsiElement psiElement) { if (psiElement instanceof PsiMethod) { final PsiMethod method = (PsiMethod)psiElement; diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java index 59505738f1ec..4f74b61750b5 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -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. - * + *

* 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. - * + *

* 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. - * + *

* 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. - * + *

* 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 processor, boolean inherited); + boolean visitMethods(Processor processor, boolean inherited, @Nullable TypeEvalContext context); + /** + * Consider using {@link PyClassLikeType#visitMembers(Processor, boolean, TypeEvalContext)} + * + * @see PyClassLikeType#visitMembers(Processor, boolean, TypeEvalContext) + */ boolean visitClassAttributes(Processor 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 own attrs are fetched, not parent attrs. * If you need parent attributes, consider using {@link #getClassAttributesInherited(TypeEvalContext)} + * * @see #getClassAttributesInherited(TypeEvalContext) */ List 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. - * + *

* 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. - * + *

* 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 */ diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java index 361ab663d6be..57a54c8cbb9f 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeType.java @@ -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 processor, boolean inherited, @NotNull TypeEvalContext context); + boolean isValid(); @Nullable diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeTypeUtil.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeTypeUtil.java new file mode 100644 index 000000000000..263a8b367c04 --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyClassLikeTypeUtil.java @@ -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 expected member type + * @return collection of members + */ + @NotNull + public static Collection getMembersOfType(@NotNull final PyClassLikeType type, + @NotNull final Class expectedMemberType, + @NotNull final TypeEvalContext context) { + + final List result = new ArrayList(); + type.visitMembers(new Processor() { + @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; + } +} diff --git a/python/src/com/jetbrains/python/PyCustomType.java b/python/src/com/jetbrains/python/PyCustomType.java index 56af324ab96a..b32d52d5df8b 100644 --- a/python/src/com/jetbrains/python/PyCustomType.java +++ b/python/src/com/jetbrains/python/PyCustomType.java @@ -241,6 +241,13 @@ public class PyCustomType implements PyClassLikeType { } } + @Override + public void visitMembers(@NotNull final Processor processor, final boolean inherited, @NotNull TypeEvalContext context) { + for (final PyClassLikeType type : myTypesToMimic) { + type.visitMembers(processor, inherited, context); + } + } + /** * Predicate that filters completion using {@link #myFilter} */ diff --git a/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java index 2985f8276b22..8f48b7f10e8e 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/PyLineMarkerProvider.java @@ -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 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) { diff --git a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java index 6f8ecba47696..6d85d760207d 100644 --- a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java @@ -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 superFunctions = getAllSuperFunctions(pyClass); - chooseAndOverrideOrImplementMethods(project, editor, pyClass, superFunctions, "Select Methods to Override", false); + final Set result = new HashSet(); + TypeEvalContext context = TypeEvalContext.codeCompletion(project, null); + final Collection 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 parameters = new ArrayList(); - 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 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 getAllSuperFunctions(@NotNull PyClass pyClass) { + public static Collection 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 superFunctions = new HashMap(); for (PyFunction function : pyClass.getMethods(true)) { if (!superFunctions.containsKey(function.getName())) { superFunctions.put(function.getName(), function); } } - return superFunctions.values(); + + + + final Set functions = new HashSet(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; } } diff --git a/python/src/com/jetbrains/python/codeInsight/testIntegration/CreateTestAction.java b/python/src/com/jetbrains/python/codeInsight/testIntegration/CreateTestAction.java index 8accdeb59e63..c4eb05808086 100644 --- a/python/src/com/jetbrains/python/codeInsight/testIntegration/CreateTestAction.java +++ b/python/src/com/jetbrains/python/codeInsight/testIntegration/CreateTestAction.java @@ -83,7 +83,7 @@ public class CreateTestAction extends PsiElementBaseIntentionAction { methods.add(pyFunction); return true; } - }, false); + }, false, null); d.methodsSize(methods.size()); int i = 0; diff --git a/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java b/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java index b4d50d6af945..946597fc29d2 100644 --- a/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java +++ b/python/src/com/jetbrains/python/findUsages/PyFindUsagesHandlerFactory.java @@ -57,7 +57,7 @@ public class PyFindUsagesHandlerFactory extends FindUsagesHandlerFactory { } if (element instanceof PyFunction) { if (!forHighlightUsages) { - final Collection superMethods = PySuperMethodsSearch.search((PyFunction)element, true).findAll(); + final Collection 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 diff --git a/python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java b/python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java index c48c4ffffbef..08a98cc7cb6d 100644 --- a/python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java +++ b/python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java @@ -128,7 +128,7 @@ public class PyStaticCallHierarchyUtil { @Nullable private static FindUsagesHandler createFindUsageHandler(@NotNull final PsiElement element) { if (element instanceof PyFunction) { - final Collection superMethods = PySuperMethodsSearch.search((PyFunction)element, true).findAll(); + final Collection 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)) { diff --git a/python/src/com/jetbrains/python/inspections/PyAbstractClassInspection.java b/python/src/com/jetbrains/python/inspections/PyAbstractClassInspection.java index 6e5a34b9194d..481db69e0594 100644 --- a/python/src/com/jetbrains/python/inspections/PyAbstractClassInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyAbstractClassInspection.java @@ -66,7 +66,7 @@ public class PyAbstractClassInspection extends PyInspection { return; } final Set toBeImplemented = new HashSet(); - final Collection functions = PyOverrideImplementUtil.getAllSuperFunctions(pyClass); + final Collection functions = PyOverrideImplementUtil.getAllSuperFunctions(pyClass, myTypeEvalContext); for (PyFunction method : functions) { if (isAbstractMethodForClass(method, pyClass)) { toBeImplemented.add(method); diff --git a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java index fccf4233fb8e..ae30508a9863 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodMayBeStaticInspection.java @@ -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; diff --git a/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java index 99093da0fb99..0c5223a74054 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodOverridingInspection.java @@ -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(); diff --git a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java index 8dd34f25f31d..0b4cfd1f2ced 100644 --- a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java @@ -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) { diff --git a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java index 14e5c78e8c81..4da7f65376cf 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java +++ b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java @@ -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; diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java index 4565cc9050c9..d973a5f43016 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyChangeSignatureQuickFix.java @@ -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; diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 0ff40fc7f636..d43805f709b5 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -552,7 +552,7 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla public PyFunction findMethodByName(@Nullable final String name, boolean inherited) { if (name == null) return null; NameFinder proc = new NameFinder(name); - visitMethods(proc, inherited); + visitMethods(proc, inherited, null); return proc.getResult(); } @@ -920,11 +920,11 @@ public class PyClassImpl extends PyBaseElementImpl implements PyCla } } - public boolean visitMethods(Processor processor, boolean inherited) { - return visitMethods(processor, inherited, false, null); + public boolean visitMethods(Processor processor, boolean inherited, @Nullable final TypeEvalContext context) { + return visitMethods(processor, inherited, false, context); } - public boolean visitMethods(Processor processor, + private boolean visitMethods(Processor processor, boolean inherited, boolean skipClassObj, TypeEvalContext context) { PyFunction[] methods = getMethods(false); @@ -934,7 +934,7 @@ public class PyClassImpl extends PyBaseElementImpl 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; } } diff --git a/python/src/com/jetbrains/python/psi/impl/references/KeywordArgumentCompletionUtil.java b/python/src/com/jetbrains/python/psi/impl/references/KeywordArgumentCompletionUtil.java index 63df3d5e3c01..e75e93fc81b3 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/KeywordArgumentCompletionUtil.java +++ b/python/src/com/jetbrains/python/psi/impl/references/KeywordArgumentCompletionUtil.java @@ -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); } diff --git a/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearch.java b/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearch.java index 2e1020e78c10..1bdf22dc9709 100644 --- a/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearch.java +++ b/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearch.java @@ -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 superMethods = new ArrayList(search(function, true).findAll()); + List superMethods = new ArrayList(search(function, true, null).findAll()); while (superMethods.size() > 0) { function = getBaseMethod(superMethods, function.getContainingClass()); - superMethods = new ArrayList(search(function, true).findAll()); + superMethods = new ArrayList(search(function, true, null).findAll()); } return function; } @@ -54,10 +56,16 @@ public class PySuperMethodsSearch extends ExtensibleQueryFactory search(final PyFunction derivedMethod) { - final SearchParameters parameters = new SearchParameters(derivedMethod, false); + public static Query search(final PyFunction derivedMethod, TypeEvalContext context) { + final SearchParameters parameters = new SearchParameters(derivedMethod, false, context); return INSTANCE.createUniqueResultsQuery(parameters); } - public static Query search(final PyFunction derivedMethod, boolean deepSearch) { - final SearchParameters parameters = new SearchParameters(derivedMethod, deepSearch); + public static Query search(final PyFunction derivedMethod, boolean deepSearch, TypeEvalContext context) { + final SearchParameters parameters = new SearchParameters(derivedMethod, deepSearch, context); return INSTANCE.createUniqueResultsQuery(parameters); } } diff --git a/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearchExecutor.java b/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearchExecutor.java index 19754493c956..6a3fc84073f5 100644 --- a/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearchExecutor.java +++ b/python/src/com/jetbrains/python/psi/search/PySuperMethodsSearchExecutor.java @@ -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 { - public boolean execute(@NotNull final PySuperMethodsSearch.SearchParameters queryParameters, @NotNull final Processor consumer) { + public boolean execute(@NotNull final PySuperMethodsSearch.SearchParameters queryParameters, + @NotNull final Processor consumer) { PyFunction func = queryParameters.getDerivedMethod(); String name = func.getName(); PyClass containingClass = func.getContainingClass(); @@ -56,6 +60,21 @@ public class PySuperMethodsSearchExecutor implements QueryExecutor findProperty(String name, AccessDirection direction, boolean inherited, @Nullable TypeEvalContext context) { + private Ref findProperty(String name, + AccessDirection direction, + boolean inherited, + @Nullable TypeEvalContext context) { Ref 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 processor, + final boolean inherited, + @NotNull final TypeEvalContext context) { + + myClass.visitMethods(new MyProcessorWrapper(processor), false, context); + myClass.visitClassAttributes(new MyProcessorWrapper(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 namesAlready, boolean suppressParentheses, List 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 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 implements Processor { + private final Processor myProcessor; + + private MyProcessorWrapper(@NotNull final Processor processor) { + myProcessor = processor; + } + + @Override + public boolean process(final T t) { + myProcessor.process(t); + return true; + } + } }