diff --git a/python/build/pycharm_build.gant b/python/build/pycharm_build.gant index a69bb2949280..2057a452d621 100644 --- a/python/build/pycharm_build.gant +++ b/python/build/pycharm_build.gant @@ -43,7 +43,8 @@ setProperty("pluginFilter", [ "python-uml", "localization", "python-localization", "rest", "python-rest", - "pycharm-flask" + "pycharm-flask", + "pycharm-numpy" ]) private List pycharmPlatformApiModules() { @@ -219,6 +220,7 @@ private layoutPlugins() { layouts.layoutPlugin("rest") layouts.layoutPlugin("python-rest") layouts.layoutPlugin("pycharm-flask") + layouts.layoutPlugin("pycharm-numpy") } layouts.layoutPlugins() diff --git a/python/main_pycharm.iml b/python/main_pycharm.iml index f0906e625e1b..d8c3dbd2074f 100644 --- a/python/main_pycharm.iml +++ b/python/main_pycharm.iml @@ -35,6 +35,7 @@ + diff --git a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java index 01216ed925c0..369894203038 100644 --- a/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java +++ b/python/pluginSrc/com/jetbrains/python/psi/impl/PyJavaTypeProvider.java @@ -50,7 +50,7 @@ public class PyJavaTypeProvider extends PyTypeProviderBase { return null; } - public PyType getParameterType(final PyNamedParameter param, final PyFunction func, TypeEvalContext context) { + public PyType getParameterType(@NotNull final PyNamedParameter param, @NotNull final PyFunction func, @NotNull TypeEvalContext context) { if (!(param.getParent() instanceof PyParameterList)) return null; List params = ParamHelper.collectNamedParameters((PyParameterList) param.getParent()); final int index = params.indexOf(param); diff --git a/python/psi-api/src/com/jetbrains/python/PyNames.java b/python/psi-api/src/com/jetbrains/python/PyNames.java index e256c462a2f2..d943f69b6d96 100644 --- a/python/psi-api/src/com/jetbrains/python/PyNames.java +++ b/python/psi-api/src/com/jetbrains/python/PyNames.java @@ -80,6 +80,11 @@ public class PyNames { public static final String SEQUENCE = "Sequence"; public static final String MAPPING = "Mapping"; + public static final String COMPLEX = "Complex"; + public static final String REAL = "Real"; + public static final String RATIONAL = "Rational"; + public static final String INTEGRAL = "Integral"; + public static final String CONTAINS = "__contains__"; public static final String HASH = "__hash__"; public static final String ITER = "__iter__"; @@ -328,7 +333,7 @@ public class PyNames { ); public static Set BuiltinInterfaces = ImmutableSet.of( - CALLABLE, HASHABLE, ITERABLE, ITERATOR, SIZED, CONTAINER, SEQUENCE, MAPPING + CALLABLE, HASHABLE, ITERABLE, ITERATOR, SIZED, CONTAINER, SEQUENCE, MAPPING, COMPLEX, REAL, RATIONAL, INTEGRAL ); /** diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyPsiFacade.java b/python/psi-api/src/com/jetbrains/python/psi/PyPsiFacade.java index 3c5773a64629..b36337576b9d 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyPsiFacade.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyPsiFacade.java @@ -7,9 +7,12 @@ import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.impl.PyQualifiedName; import com.jetbrains.python.psi.resolve.QualifiedNameResolver; import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.PyType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; + /** * @author yole */ @@ -24,8 +27,18 @@ public abstract class PyPsiFacade { @Nullable public abstract PyClass findClass(String qName); + @NotNull public abstract PyClassType createClassType(@NotNull PyClass pyClass, boolean isDefinition); @Nullable - public abstract String findShortestImportableName(PsiElement importer, VirtualFile targetFile); + public abstract PyType createUnionType(@NotNull Collection members); + + @Nullable + public abstract PyType createTupleType(@NotNull Collection members, @NotNull PsiElement anchor); + + @Nullable + public abstract PyType parseTypeAnnotation(@NotNull String annotation, @NotNull PsiElement anchor); + + @Nullable + public abstract String findShortestImportableName(@NotNull VirtualFile targetFile, @NotNull PsiElement anchor); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java index de6116ae3e20..77d89834b3d9 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/impl/PyTypeProvider.java @@ -21,10 +21,10 @@ public interface PyTypeProvider { PyType getReferenceType(@NotNull PsiElement referenceTarget, TypeEvalContext context, @Nullable PsiElement anchor); @Nullable - PyType getParameterType(PyNamedParameter param, final PyFunction func, TypeEvalContext context); + PyType getParameterType(@NotNull PyNamedParameter param, @NotNull PyFunction func, @NotNull TypeEvalContext context); @Nullable - PyType getReturnType(PyFunction function, @Nullable PyQualifiedExpression callSite, TypeEvalContext context); + PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context); @Nullable PyType getIterationType(PyClass iterable); diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java index 1f46ccb035c6..58de5ff55bfd 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyModuleMembersProvider.java @@ -21,7 +21,7 @@ public abstract class PyModuleMembersProvider { public Collection getMembers(PyFile module, PointInImport point) { final VirtualFile vFile = module.getVirtualFile(); if (vFile != null) { - final String qName = PyPsiFacade.getInstance(module.getProject()).findShortestImportableName(module, vFile); + final String qName = PyPsiFacade.getInstance(module.getProject()).findShortestImportableName(vFile, module); if (qName != null) { return getMembersByQName(module, qName); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java index 04a2ff40c895..2ae7b32b5998 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/PyTypeProviderBase.java @@ -75,12 +75,12 @@ public class PyTypeProviderBase implements PyTypeProvider { } @Override - public PyType getParameterType(PyNamedParameter param, PyFunction func, TypeEvalContext context) { + public PyType getParameterType(@NotNull PyNamedParameter param, @NotNull PyFunction func, @NotNull TypeEvalContext context) { return null; } @Override - public PyType getReturnType(PyFunction function, @Nullable PyQualifiedExpression callSite, TypeEvalContext context) { + public PyType getReturnType(@NotNull PyFunction function, @Nullable PyQualifiedExpression callSite, @NotNull TypeEvalContext context) { ReturnTypeDescriptor descriptor; synchronized (myMethodToReturnTypeMap) { descriptor = myMethodToReturnTypeMap.get(function.getName()); diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 015accf5176c..1bcda3178fcf 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -104,7 +104,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { @Nullable @Override - public PyType getParameterType(PyNamedParameter param, @NotNull PyFunction func, @NotNull TypeEvalContext context) { + public PyType getParameterType(@NotNull PyNamedParameter param, @NotNull PyFunction func, @NotNull TypeEvalContext context) { final String name = param.getName(); final String qname = getQualifiedName(func, param); if (qname != null && name != null) { diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/StdlibTypes2.properties b/python/src/com/jetbrains/python/codeInsight/stdlib/StdlibTypes2.properties index 21f9ab00484f..673d3198fd80 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/StdlibTypes2.properties +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/StdlibTypes2.properties @@ -3,7 +3,6 @@ ## 2. Built-in functions __builtin__.abs = \ - :type number: int or long or float or complex \n\ :rtype: int \n\ __builtin__.all = \ @@ -1577,72 +1576,72 @@ collections.Iterator.next = \ ## 9.2 math math.acos = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.acosh = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.asin = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.asinh = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.atan = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.atan2 = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.atanh = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.ceil = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.copysign = \ - :type x: int or long or float \n\ - :type y: int or long or float \n\ + :type x: float or numbers.Real \n\ + :type y: float or numbers.Real \n\ :rtype: float \n\ math.cos = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.cosh = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.degrees = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.erf = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.erfc = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.exp = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.expm1 = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.fabs = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.factorial = \ @@ -1650,16 +1649,16 @@ math.factorial = \ :rtype: int \n\ math.floor = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.fmod = \ - :type x: int or long or float \n\ - :type y: int or long or float \n\ + :type x: float or numbers.Real \n\ + :type y: float or numbers.Real \n\ :rtype: float \n\ math.frexp = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: (float, int) \n\ math.fsum = \ @@ -1667,79 +1666,79 @@ math.fsum = \ :rtype: float \n\ math.gamma = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.hypot = \ - :type x: int or long or float \n\ - :type y: int or long or float \n\ + :type x: float or numbers.Real \n\ + :type y: float or numbers.Real \n\ :rtype: float \n\ math.isinf = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: bool \n\ math.isnan = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: bool \n\ math.ldexp = \ - :type x: int or long or float \n\ - :type y: int or long or float \n\ + :type x: float or numbers.Real \n\ + :type y: float or numbers.Real \n\ :rtype: float \n\ math.lgamma = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.log = \ - :type x: int or long or float \n\ - :type base: int or long or float \n\ + :type x: float or numbers.Real \n\ + :type base: float or numbers.Real \n\ :rtype: float \n\ math.log10 = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.log1p = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.modf = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: (float, float) \n\ math.pow = \ - :type x: int or long or float \n\ - :type y: int or long or float \n\ + :type x: float or numbers.Real \n\ + :type y: float or numbers.Real \n\ :rtype: float \n\ math.radians = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.sin = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.sinh = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.sqrt = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.tan = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.tanh = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: float \n\ math.trunc = \ - :type x: int or long or float \n\ + :type x: float or numbers.Real \n\ :rtype: int \n\ diff --git a/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java b/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java index 19d9f5009934..6a3f6b55a5f6 100644 --- a/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java +++ b/python/src/com/jetbrains/python/debugger/PyCallSignatureTypeProvider.java @@ -3,12 +3,13 @@ package com.jetbrains.python.debugger; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyNamedParameter; import com.jetbrains.python.psi.types.*; +import org.jetbrains.annotations.NotNull; /** * @author traff */ public class PyCallSignatureTypeProvider extends PyTypeProviderBase { - public PyType getParameterType(final PyNamedParameter param, final PyFunction func, TypeEvalContext context) { + public PyType getParameterType(@NotNull final PyNamedParameter param, @NotNull final PyFunction func, @NotNull TypeEvalContext context) { final String name = param.getName(); if (name != null) { final String typeName = ((PySignatureCacheManagerImpl)PySignatureCacheManager.getInstance(param.getProject())).findParameterType(func, name); diff --git a/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java b/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java index 24f1f8b3147c..944a95fdaa67 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java @@ -318,8 +318,7 @@ public class PyBuiltinCache { synchronized (myStdlibTypeCache) { final Ref ref = myStdlibTypeCache.get(key); if (ref != null) { - final PyType pyType = ref.get(); - if (pyType instanceof PyClassType && !((PyClassType)pyType).isValid()) { + if (!isValid(ref.get())) { myStdlibTypeCache.clear(); return null; } @@ -328,6 +327,31 @@ public class PyBuiltinCache { } } + private static boolean isValid(@Nullable PyType type) { + if (type instanceof PyCollectionType) { + final PyType elementType = ((PyCollectionType)type).getElementType(TypeEvalContext.fastStubOnly(null)); + if (!isValid(elementType)) { + return false; + } + } + + if (type instanceof PyClassType) { + return ((PyClassType)type).isValid(); + } + else if (type instanceof PyUnionType) { + for (PyType member : ((PyUnionType)type).getMembers()) { + if (!isValid(member)) { + return false; + } + } + return true; + } + else if (type instanceof PyFunctionType) { + return ((PyFunctionType)type).getFunction().isValid(); + } + return true; + } + public void storeStdlibType(@NotNull String key, @Nullable PyType result) { synchronized (myStdlibTypeCache) { myStdlibTypeCache.put(key, new Ref(result)); diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index df7e136264a1..1859530767de 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -204,7 +204,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp } @Nullable - private PyType getGenericReturnType(TypeEvalContext typeEvalContext, @Nullable PyQualifiedExpression callSite) { + private PyType getGenericReturnType(@NotNull TypeEvalContext typeEvalContext, @Nullable PyQualifiedExpression callSite) { if (typeEvalContext.maySwitchToAST(this)) { PyAnnotation anno = getAnnotation(); if (anno != null) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyPsiFacadeImpl.java b/python/src/com/jetbrains/python/psi/impl/PyPsiFacadeImpl.java index f5b9e99e1414..1faa83cf8681 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyPsiFacadeImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyPsiFacadeImpl.java @@ -9,11 +9,12 @@ import com.jetbrains.python.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.psi.resolve.QualifiedNameResolver; import com.jetbrains.python.psi.resolve.QualifiedNameResolverImpl; import com.jetbrains.python.psi.stubs.PyClassNameIndex; -import com.jetbrains.python.psi.types.PyClassType; -import com.jetbrains.python.psi.types.PyClassTypeImpl; +import com.jetbrains.python.psi.types.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; + /** * @author yole */ @@ -40,6 +41,7 @@ public class PyPsiFacadeImpl extends PyPsiFacade { return PyClassNameIndex.findClass(qName, myProject); } + @NotNull @Override public PyClassType createClassType(@NotNull PyClass pyClass, boolean isDefinition) { return new PyClassTypeImpl(pyClass, isDefinition); @@ -47,7 +49,25 @@ public class PyPsiFacadeImpl extends PyPsiFacade { @Nullable @Override - public String findShortestImportableName(PsiElement importer, VirtualFile targetFile) { - return QualifiedNameFinder.findShortestImportableName(importer, targetFile); + public PyType createUnionType(@NotNull Collection members) { + return PyUnionType.union(members); + } + + @Nullable + @Override + public PyType createTupleType(@NotNull Collection members, @NotNull PsiElement anchor) { + return PyTupleType.create(anchor, members.toArray(new PyType[members.size()])); + } + + @Nullable + @Override + public PyType parseTypeAnnotation(@NotNull String annotation, @NotNull PsiElement anchor) { + return PyTypeParser.getTypeByName(anchor, annotation); + } + + @Nullable + @Override + public String findShortestImportableName(@NotNull VirtualFile targetFile, @NotNull PsiElement anchor) { + return QualifiedNameFinder.findShortestImportableName(anchor, targetFile); } } diff --git a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java index 6784a4f5b509..3914f94aad4e 100644 --- a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java +++ b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java @@ -31,7 +31,7 @@ public class QualifiedNameFinder { * the name with fewest qualifiers is selected. */ @Nullable - public static String findShortestImportableName(PsiElement foothold, @NotNull VirtualFile vfile) { + public static String findShortestImportableName(@NotNull PsiElement foothold, @NotNull VirtualFile vfile) { final PyQualifiedName qName = findShortestImportableQName(foothold, vfile); return qName == null ? null : qName.toString(); } diff --git a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java index 6192436f2415..21833152f3ca 100644 --- a/python/src/com/jetbrains/python/psi/types/PyABCUtil.java +++ b/python/src/com/jetbrains/python/psi/types/PyABCUtil.java @@ -53,6 +53,15 @@ public class PyABCUtil { if (PyNames.MAPPING.equals(superClassName)) { return isSized && hasIter && isContainer && hasGetItem && hasMethod(subClass, PyNames.KEYS, inherited); } + if (PyNames.COMPLEX.equals(superClassName)) { + return hasMethod(subClass, "__complex__", inherited); + } + if (PyNames.REAL.equals(superClassName)) { + return hasMethod(subClass, "__float__", inherited); + } + if (PyNames.INTEGRAL.equals(superClassName)) { + return hasMethod(subClass, "__int__", inherited); + } return false; } diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index cee945351bdc..ce0f4de96a15 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -50,9 +50,6 @@ public class PyTypeChecker { if (actual instanceof PyTypeReference) { return match(expected, ((PyTypeReference)actual).resolve(null, context), context, substitutions, false); } - if (isUnknown(actual)) { - return true; - } if (expected instanceof PyGenericType && substitutions != null) { final PyGenericType generic = (PyGenericType)expected; final PyType subst = substitutions.get(generic); @@ -72,6 +69,9 @@ public class PyTypeChecker { return true; } } + if (isUnknown(actual)) { + return true; + } if (actual instanceof PyUnionType) { for (PyType m : ((PyUnionType)actual).getMembers()) { if (!match(expected, m, context, substitutions, recursive)) { @@ -149,7 +149,7 @@ public class PyTypeChecker { } public static boolean isUnknown(@Nullable PyType type) { - if (type == null || type instanceof PyTypeReference) { + if (type == null || type instanceof PyTypeReference || type instanceof PyGenericType) { return true; } if (type instanceof PyUnionType) { @@ -165,29 +165,34 @@ public class PyTypeChecker { public static boolean hasGenerics(@Nullable PyType type, @NotNull TypeEvalContext context) { final Set collected = new HashSet(); - collectGenerics(type, context, collected); + collectGenerics(type, context, collected, new HashSet()); return !collected.isEmpty(); } - private static void collectGenerics(@Nullable PyType type, @NotNull TypeEvalContext context, @NotNull Set collected) { + private static void collectGenerics(@Nullable PyType type, @NotNull TypeEvalContext context, @NotNull Set collected, + @NotNull Set visited) { + if (visited.contains(type)) { + return; + } + visited.add(type); if (type instanceof PyGenericType) { collected.add((PyGenericType)type); } else if (type instanceof PyUnionType) { final PyUnionType union = (PyUnionType)type; for (PyType t : union.getMembers()) { - collectGenerics(t, context, collected); + collectGenerics(t, context, collected, visited); } } else if (type instanceof PyCollectionType) { final PyCollectionType collection = (PyCollectionType)type; - collectGenerics(collection.getElementType(context), context, collected); + collectGenerics(collection.getElementType(context), context, collected, visited); } else if (type instanceof PyTupleType) { final PyTupleType tuple = (PyTupleType)type; final int n = tuple.getElementCount(); for (int i = 0; i < n; i++) { - collectGenerics(tuple.getElementType(i), context, collected); + collectGenerics(tuple.getElementType(i), context, collected, visited); } } } @@ -252,7 +257,7 @@ public class PyTypeChecker { // Collect generic params of object type final Set generics = new LinkedHashSet(); final PyType qualifierType = receiver != null ? receiver.getType(context) : null; - collectGenerics(qualifierType, context, generics); + collectGenerics(qualifierType, context, generics, new HashSet()); for (PyGenericType t : generics) { substitutions.put(t, t); } diff --git a/python/testData/inspections/PyTypeCheckerInspection/GenericArguments.py b/python/testData/inspections/PyTypeCheckerInspection/GenericArguments.py new file mode 100644 index 000000000000..b7fc3e6b4416 --- /dev/null +++ b/python/testData/inspections/PyTypeCheckerInspection/GenericArguments.py @@ -0,0 +1,18 @@ +class B(object): + def __init__(self, x): + """ + :type x: T + :rtype: B of T + """ + self._x = x + + def foo(self): + """ + :rtype: T + """ + return self._x + +class C(B): + def bar(self): + expr = self.foo() + return 'foo' + expr #pass \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/attributesOfGenerics.py b/python/testData/inspections/PyUnresolvedReferencesInspection/attributesOfGenerics.py new file mode 100644 index 000000000000..d424c822ad42 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/attributesOfGenerics.py @@ -0,0 +1,18 @@ +class B(object): + def __init__(self, x): + """ + :type x: T + :rtype: B of T + """ + self._x = x + + def foo(self): + """ + :rtype: T + """ + return self._x + +class C(B): + def bar(self): + expr = self.foo() + return expr.upper() #pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java index 0169210658f1..2f8009f33a7c 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyTypeCheckerInspectionTest.java @@ -140,4 +140,9 @@ public class PyTypeCheckerInspectionTest extends PyTestCase { public void testAssignedOperator() { doTest(); } + + // PY-7244 + public void testGenericArguments() { + doTest(); + } } diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 64886dc36c81..b4a532e133df 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -175,6 +175,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + // PY-7244 + public void testAttributesOfGenerics() { + doTest(); + } + private void doTest() { myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py"); myFixture.enableInspections(PyUnresolvedReferencesInspection.class);