diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java new file mode 100644 index 000000000000..d13a0c9d1280 --- /dev/null +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaMethodType.java @@ -0,0 +1,69 @@ +package com.jetbrains.python.psi.impl; + +import com.intellij.psi.PsiMethod; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ProcessingContext; +import com.jetbrains.python.psi.AccessDirection; +import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.PyQualifiedExpression; +import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.resolve.RatedResolveResult; +import com.jetbrains.python.psi.types.PyCallableType; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +/** + * @author yole + */ +public class PyJavaMethodType implements PyCallableType { + private final PsiMethod myMethod; + + public PyJavaMethodType(PsiMethod method) { + myMethod = method; + } + + @Override + public boolean isCallable() { + return true; + } + + @Nullable + @Override + public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { + return PyJavaTypeProvider.asPyType(myMethod.getReturnType()); + } + + @Nullable + @Override + public List resolveMember(@NotNull String name, + @Nullable PyExpression location, + AccessDirection direction, + PyResolveContext resolveContext) { + return Collections.emptyList(); + } + + @Override + public Object[] getCompletionVariants(String completionPrefix, PyExpression location, ProcessingContext context) { + return ArrayUtil.EMPTY_OBJECT_ARRAY; + } + + @Nullable + @Override + public String getName() { + return "Java method(" + myMethod.getContainingClass().getQualifiedName() + "." + myMethod.getName() + ")"; + } + + @Override + public boolean isBuiltin(TypeEvalContext context) { + return false; + } + + @Override + public void assertValid(String message) { + } +} diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java index 9d186f315c9e..5ecf0d86d2f6 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java @@ -30,7 +30,7 @@ public class PyJavaTypeProvider extends PyTypeProviderBase { } if (referenceTarget instanceof PsiMethod) { PsiMethod method = (PsiMethod) referenceTarget; - return asPyType(method.getReturnType()); + return new PyJavaMethodType(method); } if (referenceTarget instanceof PsiField) { return asPyType(((PsiField)referenceTarget).getType()); @@ -39,7 +39,7 @@ public class PyJavaTypeProvider extends PyTypeProviderBase { } @Nullable - private static PyType asPyType(PsiType type) { + public static PyType asPyType(PsiType type) { if (type instanceof PsiClassType) { final PsiClassType classType = (PsiClassType)type; final PsiClass psiClass = classType.resolve(); diff --git a/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java b/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java index 8ddd01f29daf..b7a65df539bd 100644 --- a/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java +++ b/python/pluginTestSrc/com/jetbrains/jython/PyJythonHighlightingTest.java @@ -3,14 +3,23 @@ package com.jetbrains.jython; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import com.jetbrains.python.PythonTestUtil; import com.jetbrains.python.inspections.PyCallingNonCallableInspection; +import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection; /** * @author yole */ public class PyJythonHighlightingTest extends LightCodeInsightFixtureTestCase { public void testCallableJavaClass() { - myFixture.configureByFile("callableJavaClass.py"); - myFixture.enableInspections(PyCallingNonCallableInspection.class); + doCallableTest(); + } + + public void testCallableStaticMethod() { + doCallableTest(); + } + + private void doCallableTest() { + myFixture.configureByFile(getTestName(false) + ".py"); + myFixture.enableInspections(PyCallingNonCallableInspection.class, PyUnresolvedReferencesInspection.class); myFixture.checkHighlighting(true, false, false); } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java index 2c824e67048d..858ea2712712 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyNamedTupleType.java @@ -25,16 +25,20 @@ import java.util.List; */ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType { private final String myName; - private final boolean myDefinition; + + // 2 - namedtuple call itself + // 1 - return type of namedtuple call, aka namedtuple class + // 0 - namedtuple instance + private final int myDefinitionLevel; private final PsiElement myDeclaration; private final List myFields; - public PyNamedTupleType(PyClass tupleClass, PsiElement declaration, String name, List fields, boolean isDefinition) { - super(tupleClass, isDefinition); + public PyNamedTupleType(PyClass tupleClass, PsiElement declaration, String name, List fields, int definitionLevel) { + super(tupleClass, definitionLevel > 0); myDeclaration = declaration; myFields = fields; myName = name; - myDefinition = isDefinition; + myDefinitionLevel = definitionLevel; } @Override @@ -75,15 +79,15 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType @Nullable @Override public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) { - if (myDefinition) { - return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, false); + if (myDefinitionLevel > 0) { + return new PyNamedTupleType(myClass, myDeclaration, myName, myFields, myDefinitionLevel-1); } return null; } @Override public PyClassType toInstance() { - return myIsDefinition ? new PyNamedTupleType(myClass, myDeclaration, myName, myFields, false) : this; + return myDefinitionLevel == 1 ? new PyNamedTupleType(myClass, myDeclaration, myName, myFields, 0) : this; } @Override @@ -92,7 +96,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType } @Nullable - public static PyType fromCall(PyCallExpression call) { + public static PyType fromCall(PyCallExpression call, int level) { final String name = PyPsiUtils.strValue(call.getArgument(0, PyExpression.class)); final PyExpression fieldNamesExpression = PyPsiUtils.flattenParens(call.getArgument(1, PyExpression.class)); if (name == null || fieldNamesExpression == null) { @@ -111,7 +115,7 @@ public class PyNamedTupleType extends PyClassTypeImpl implements PyCallableType if (fieldNames != null) { PyClass tuple = PyBuiltinCache.getInstance(call).getClass(PyNames.FAKE_NAMEDTUPLE); if (tuple != null) { - return new PyNamedTupleType(tuple, call, name, fieldNames, true); + return new PyNamedTupleType(tuple, call, name, fieldNames, level); } } return null; diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 9802764908e4..dcceff5271c7 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -58,7 +58,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { if (callee != null) { final Callable callable = callee.getCallable(); if (PyNames.COLLECTIONS_PY.equals(callable.getContainingFile().getName())) { - return PyNamedTupleType.fromCall(call); + return PyNamedTupleType.fromCall(call, 1); } } } @@ -67,7 +67,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { else if (referenceTarget instanceof PyFunction && anchor instanceof PyCallExpression) { if (PyNames.NAMEDTUPLE.equals(((PyFunction)referenceTarget).getName()) && PyNames.COLLECTIONS_PY.equals(referenceTarget.getContainingFile().getName())) { - return PyNamedTupleType.fromCall((PyCallExpression)anchor); + return PyNamedTupleType.fromCall((PyCallExpression)anchor, 2); } } return null; diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java index fee8f42449c1..844a41a1576a 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionHelper.java @@ -419,8 +419,8 @@ public class PyCallExpressionHelper { return new PyClassTypeImpl(cls, false); } final PyType providedType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(target, context, call); - if (providedType != null) { - return providedType; + if (providedType instanceof PyCallableType) { + return ((PyCallableType) providedType).getCallType(context, (PyReferenceExpression)callee); } if (target instanceof Callable) { final Callable callable = (Callable)target; diff --git a/python/testData/highlighting/jython/callableStaticMethod.py b/python/testData/highlighting/jython/callableStaticMethod.py new file mode 100644 index 000000000000..59ff5a8945f7 --- /dev/null +++ b/python/testData/highlighting/jython/callableStaticMethod.py @@ -0,0 +1,4 @@ +from java.util import Collections + +x = Collections.emptyList() +s = x.size()