@NotNull private static List<PyGenericType> collectGenericTypes( @NotNull PyClass cls, @NotNull Context context) { boolean isGeneric = false; for (PyClass ancestor : cls.getAncestorClasses(context.getTypeContext())) { if (GENERIC_CLASSES.contains(ancestor.getQualifiedName())) { isGeneric = true; break; } } if (isGeneric) { final ArrayList<PyGenericType> results = new ArrayList<>(); // XXX: Requires switching from stub to AST for (PyExpression expr : cls.getSuperClassExpressions()) { if (expr instanceof PySubscriptionExpression) { final PyExpression indexExpr = ((PySubscriptionExpression) expr).getIndexExpression(); if (indexExpr != null) { for (PsiElement resolved : tryResolving(indexExpr, context.getTypeContext())) { final PyGenericType genericType = getGenericType(resolved, context); if (genericType != null) { results.add(genericType); } } } } } return results; } return Collections.emptyList(); }
@Nullable private static PyGenericType getGenericType( @NotNull PsiElement element, @NotNull Context context) { if (element instanceof PyCallExpression) { final PyCallExpression assignedCall = (PyCallExpression) element; final PyExpression callee = assignedCall.getCallee(); if (callee != null) { final Collection<String> calleeQNames = resolveToQualifiedNames(callee, context.getTypeContext()); if (calleeQNames.contains("typing.TypeVar")) { final PyExpression[] arguments = assignedCall.getArguments(); if (arguments.length > 0) { final PyExpression firstArgument = arguments[0]; if (firstArgument instanceof PyStringLiteralExpression) { final String name = ((PyStringLiteralExpression) firstArgument).getStringValue(); if (name != null) { return new PyGenericType(name, getGenericTypeBound(arguments, context)); } } } } } } return null; }
@Nullable private static PyType getCallableType(@NotNull PsiElement resolved, @NotNull Context context) { if (resolved instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression) resolved; final PyExpression operand = subscriptionExpr.getOperand(); final Collection<String> operandNames = resolveToQualifiedNames(operand, context.getTypeContext()); if (operandNames.contains("typing.Callable")) { final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); if (indexExpr instanceof PyTupleExpression) { final PyTupleExpression tupleExpr = (PyTupleExpression) indexExpr; final PyExpression[] elements = tupleExpr.getElements(); if (elements.length == 2) { final PyExpression parametersExpr = elements[0]; final PyExpression returnTypeExpr = elements[1]; if (parametersExpr instanceof PyListLiteralExpression) { final List<PyCallableParameter> parameters = new ArrayList<>(); final PyListLiteralExpression listExpr = (PyListLiteralExpression) parametersExpr; for (PyExpression argExpr : listExpr.getElements()) { parameters.add(new PyCallableParameterImpl(null, getType(argExpr, context))); } final PyType returnType = getType(returnTypeExpr, context); return new PyCallableTypeImpl(parameters, returnType); } if (isEllipsis(parametersExpr)) { return new PyCallableTypeImpl(null, getType(returnTypeExpr, context)); } } } } } return null; }
@Nullable private static PyType getTypeForResolvedElement( @NotNull PsiElement resolved, @NotNull Context context) { if (context.getExpressionCache().contains(resolved)) { // Recursive types are not yet supported return null; } context.getExpressionCache().add(resolved); try { final PyType unionType = getUnionType(resolved, context); if (unionType != null) { return unionType; } final Ref<PyType> optionalType = getOptionalType(resolved, context); if (optionalType != null) { return optionalType.get(); } final PyType callableType = getCallableType(resolved, context); if (callableType != null) { return callableType; } final PyType parameterizedType = getParameterizedType(resolved, context); if (parameterizedType != null) { return parameterizedType; } final PyType builtinCollection = getBuiltinCollection(resolved); if (builtinCollection != null) { return builtinCollection; } final PyType genericType = getGenericType(resolved, context); if (genericType != null) { return genericType; } final Ref<PyType> classType = getClassType(resolved, context.getTypeContext()); if (classType != null) { return classType.get(); } final PyType stringBasedType = getStringBasedType(resolved, context); if (stringBasedType != null) { return stringBasedType; } return null; } finally { context.getExpressionCache().remove(resolved); } }
@Nullable private static PyType getType(@NotNull PyExpression expression, @NotNull Context context) { final List<PyType> members = Lists.newArrayList(); for (PsiElement resolved : tryResolving(expression, context.getTypeContext())) { members.add(getTypeForResolvedElement(resolved, context)); } return PyUnionType.union(members); }
@Nullable private static PyType getUnionType(@NotNull PsiElement element, @NotNull Context context) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression) element; final PyExpression operand = subscriptionExpr.getOperand(); final Collection<String> operandNames = resolveToQualifiedNames(operand, context.getTypeContext()); if (operandNames.contains("typing.Union")) { return PyUnionType.union(getIndexTypes(subscriptionExpr, context)); } } return null; }
@Nullable private static Ref<PyType> getOptionalType( @NotNull PsiElement element, @NotNull Context context) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression) element; final PyExpression operand = subscriptionExpr.getOperand(); final Collection<String> operandNames = resolveToQualifiedNames(operand, context.getTypeContext()); if (operandNames.contains("typing.Optional")) { final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); if (indexExpr != null) { final PyType type = getType(indexExpr, context); if (type != null) { return Ref.create(PyUnionType.union(type, PyNoneType.INSTANCE)); } } return Ref.create(); } } return null; }