@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(); }
private static List<Token<PyElementType>> tokenize(@NotNull String s) { final List<Token<PyElementType>> tokens = new ArrayList<>(); final _PyTypeLexer lexer = new _PyTypeLexer(new StringReader(s)); lexer.reset(s, 0, s.length(), lexer.yystate()); try { PyElementType type; while ((type = lexer.advance()) != null) { if (type == PyTypeTokenTypes.SPACE || type == PyTypeTokenTypes.MARKUP) { continue; } final TextRange range = TextRange.create(lexer.getTokenStart(), lexer.getTokenEnd()); final Token<PyElementType> token = new Token<>(type, lexer.yytext(), range); tokens.add(token); } } catch (IOException e) { return Collections.emptyList(); } catch (Error e) { return Collections.emptyList(); } return tokens; }
@Override public List<PyExpression> getQualifiers() { return Collections.emptyList(); }
@NotNull @Override protected Collection<PyElement> getDependencies( @NotNull final MultiMap<PyClass, PyElement> usedElements) { return Collections.emptyList(); }
@NotNull public static ParseResult parsePep484FunctionTypeComment( @NotNull PsiElement anchor, @NotNull String text) { final ForwardDeclaration<ParseResult, PyElementType> typeExpr = ForwardDeclaration.create(); final Function<Pair<ParseResult, List<ParseResult>>, ParseResult> toParamTypeList = pair -> { if (pair != null) { final ParseResult first = pair.getFirst(); final List<ParseResult> second = pair.getSecond(); final List<PyType> itemTypes = new ArrayList<>(); ParseResult result = first; itemTypes.add(result.getType()); for (ParseResult r : second) { result = result.merge(r); itemTypes.add(r.getType()); } return result.withType(ParameterListType.fromParameterTypes(itemTypes)); } return EMPTY_RESULT.withType(new ParameterListType(Collections.emptyList())); }; final FunctionalParser<ParseResult, PyElementType> ellipsis = op("...").map(token -> EMPTY_RESULT.withType(EllipsisType.INSTANCE)).named("ellipsis"); final FunctionalParser<ParseResult, PyElementType> classType = token(IDENTIFIER) .then(many(op(".").skipThen(token(IDENTIFIER)))) .map(new MakeSimpleType(anchor)) .cached() .named("class-type"); final FunctionalParser<ParseResult, PyElementType> typeParamList = op("[") .skipThen(maybe(typeExpr.then(many(op(",").skipThen(typeExpr))))) .thenSkip(op("]")) .map(toParamTypeList) .named("type-param-list"); final FunctionalParser<ParseResult, PyElementType> typeParam = typeExpr.or(typeParamList).or(ellipsis).named("type-param"); final FunctionalParser<ParseResult, PyElementType> genericType = classType .thenSkip(op("[")) .then(typeParam) .then(many(op(",").skipThen(typeParam))) .thenSkip(op("]")) .map( value -> { final Pair<ParseResult, ParseResult> firstPair = value.getFirst(); final ParseResult first = firstPair.getFirst(); final ParseResult second = firstPair.getSecond(); final List<ParseResult> third = value.getSecond(); final List<PyType> typesInBrackets = new ArrayList<>(); typesInBrackets.add(second.getType()); ParseResult result = first; result = result.merge(second); for (ParseResult r : third) { typesInBrackets.add(r.getType()); result = result.merge(r); } final List<PyType> elementTypes = third.isEmpty() ? Collections.singletonList(second.getType()) : typesInBrackets; final PsiElement resolved = first.getElement(); if (resolved != null) { final PyType typingType = PyTypingTypeProvider.getType(resolved, elementTypes); if (typingType != null) { return result.withType(typingType); } } return EMPTY_RESULT; }) .named("generic-type"); typeExpr.define(genericType.or(classType)).named("type-expr"); final FunctionalParser<ParseResult, PyElementType> paramType = maybe(op("*")) .then(maybe(op("*"))) .then(typeExpr) .map( pair -> { final ParseResult paramResult = pair.getSecond(); final PyType type = paramResult.getType(); int starCount = 0; if (pair.getFirst().getFirst() != null) { starCount++; } if (pair.getFirst().getSecond() != null) { starCount++; } if (starCount == 0) { return paramResult; } else if (starCount == 1) { final PyClassType tupleType = PyTupleType.createHomogeneous(anchor, type); if (tupleType != null) { return paramResult.withType(tupleType); } return EMPTY_RESULT; } else if (starCount == 2) { final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(anchor); final PyClassType dictType = builtinCache.getDictType(); if (dictType != null) { final PyClass pyClass = dictType.getPyClass(); return paramResult.withType( new PyCollectionTypeImpl( pyClass, false, Arrays.asList(builtinCache.getStrType(), type))); } return EMPTY_RESULT; } return EMPTY_RESULT; }) .named("param-type"); final FunctionalParser<ParseResult, PyElementType> paramTypes = paramType.then(many(op(",").skipThen(paramType))).map(toParamTypeList).named("param-types"); final FunctionalParser<ParseResult, PyElementType> funcType = op("(") .skipThen(maybe(paramTypes.or(ellipsis))) .thenSkip(op(")")) .thenSkip(op("->")) .then(typeExpr) .map( value -> { final ParseResult paramsResult = value.getFirst(); final ParseResult returnResult = value.getSecond(); final List<PyCallableParameter> parameters; ParseResult result = returnResult; if (paramsResult != null) { result = result.merge(paramsResult); final ParameterListType paramTypesList = as(paramsResult.getType(), ParameterListType.class); if (paramTypesList != null) { parameters = paramTypesList.getCallableParameters(); } // ellipsis else { parameters = null; } } else { parameters = Collections.emptyList(); result = returnResult; } return result.withType( new PyCallableTypeImpl(parameters, returnResult.getType())); }) .named("func-type"); final FunctionalParser<ParseResult, PyElementType> typeFile = funcType.endOfInput().named("function-type-comment"); try { return typeFile.parse(tokenize(text)); } catch (ParserException e) { return EMPTY_RESULT; } }