@Nullable public static PyType getType(@NotNull PsiElement resolved, @NotNull List<PyType> elementTypes) { final String qualifiedName = getQualifiedName(resolved); final List<Integer> paramListTypePositions = new ArrayList<>(); final List<Integer> ellipsisTypePositions = new ArrayList<>(); for (int i = 0; i < elementTypes.size(); i++) { final PyType type = elementTypes.get(i); if (type instanceof PyTypeParser.ParameterListType) { paramListTypePositions.add(i); } else if (type instanceof PyTypeParser.EllipsisType) { ellipsisTypePositions.add(i); } } if (!paramListTypePositions.isEmpty()) { if (!("typing.Callable".equals(qualifiedName) && paramListTypePositions.equals(list(0)))) { return null; } } if (!ellipsisTypePositions.isEmpty()) { if (!("typing.Callable".equals(qualifiedName) && ellipsisTypePositions.equals(list(0)) || "typing.Tuple".equals(qualifiedName) && ellipsisTypePositions.equals(list(1)) && elementTypes.size() == 2)) { return null; } } if ("typing.Union".equals(qualifiedName)) { return PyUnionType.union(elementTypes); } if ("typing.Optional".equals(qualifiedName) && elementTypes.size() == 1) { return PyUnionType.union(elementTypes.get(0), PyNoneType.INSTANCE); } if ("typing.Callable".equals(qualifiedName) && elementTypes.size() == 2) { final PyTypeParser.ParameterListType paramList = as(elementTypes.get(0), PyTypeParser.ParameterListType.class); if (paramList != null) { return new PyCallableTypeImpl(paramList.getCallableParameters(), elementTypes.get(1)); } if (elementTypes.get(0) instanceof PyTypeParser.EllipsisType) { return new PyCallableTypeImpl(null, elementTypes.get(1)); } } if ("typing.Tuple".equals(qualifiedName)) { if (elementTypes.size() > 1 && elementTypes.get(1) instanceof PyTypeParser.EllipsisType) { return PyTupleType.createHomogeneous(resolved, elementTypes.get(0)); } return PyTupleType.create(resolved, elementTypes); } final PyType builtinCollection = getBuiltinCollection(resolved); if (builtinCollection instanceof PyClassType) { final PyClassType classType = (PyClassType) builtinCollection; return new PyCollectionTypeImpl(classType.getPyClass(), false, elementTypes); } return null; }
@Nullable private static PyType getParameterizedType( @NotNull PsiElement element, @NotNull Context context) { if (element instanceof PySubscriptionExpression) { final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression) element; final PyExpression operand = subscriptionExpr.getOperand(); final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); final PyType operandType = getType(operand, context); if (operandType instanceof PyClassType) { final PyClass cls = ((PyClassType) operandType).getPyClass(); final List<PyType> indexTypes = getIndexTypes(subscriptionExpr, context); if (PyNames.TUPLE.equals(cls.getQualifiedName())) { if (indexExpr instanceof PyTupleExpression) { final PyExpression[] elements = ((PyTupleExpression) indexExpr).getElements(); if (elements.length == 2 && isEllipsis(elements[1])) { return PyTupleType.createHomogeneous(element, indexTypes.get(0)); } } return PyTupleType.create(element, indexTypes); } else if (indexExpr != null) { return new PyCollectionTypeImpl(cls, false, indexTypes); } } } 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 PyType resolveQualifierType( @NotNull List<Token<PyElementType>> tokens, @NotNull PyFile file, @NotNull TypeEvalContext context, @NotNull Map<TextRange, PyType> types, @NotNull Map<PyType, TextRange> fullRanges, @NotNull Map<PyType, PyImportElement> imports) { if (tokens.isEmpty()) { return null; } final Token<PyElementType> firstToken = tokens.get(0); final String firstText = firstToken.getText().toString(); final TextRange firstRange = firstToken.getRange(); final List<RatedResolveResult> resolveResults = file.multiResolveName(firstText); if (resolveResults.isEmpty()) { return getImplicitlyResolvedType(tokens, context, types, fullRanges, firstRange); } final List<PyType> members = Lists.newArrayList(); for (RatedResolveResult result : resolveResults) { final PsiElement resolved = result.getElement(); PyType type = null; if (resolved instanceof PyTargetExpression) { type = PyTypingTypeProvider.getTypeFromTargetExpression( (PyTargetExpression) resolved, context); } if (type == null && resolved instanceof PyTypedElement) { type = context.getType((PyTypedElement) resolved); } if (type != null) { if (!allowResolveToType(type)) { continue; } if (type instanceof PyClassLikeType) { type = ((PyClassLikeType) type).toInstance(); } types.put(firstRange, type); fullRanges.put(type, firstRange); for (PyFromImportStatement fromImportStatement : file.getFromImports()) { for (PyImportElement importElement : fromImportStatement.getImportElements()) { if (firstText.equals(importElement.getVisibleName())) { imports.put(type, importElement); } } } for (PyImportElement importElement : file.getImportTargets()) { if (firstText.equals(importElement.getVisibleName())) { imports.put(type, importElement); } } } members.add(type); } if (!members.isEmpty()) { tokens.remove(0); } return PyUnionType.union(members); }
@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 getGenericTypeBound( @NotNull PyExpression[] typeVarArguments, @NotNull Context context) { final List<PyType> types = new ArrayList<>(); for (int i = 1; i < typeVarArguments.length; i++) { types.add(getType(typeVarArguments[i], context)); } return PyUnionType.union(types); }
public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { if (!TypeEvalStack.mayEvaluate(this)) { return null; } try { final boolean qualified = isQualified(); if (!qualified) { String name = getReferencedName(); if (PyNames.NONE.equals(name)) { return PyNoneType.INSTANCE; } } PyType type = getTypeFromProviders(context); if (type != null) { return type; } if (qualified) { PyType maybe_type = PyUtil.getSpecialAttributeType(this, context); if (maybe_type != null) return maybe_type; Ref<PyType> typeOfProperty = getTypeOfProperty(context); if (typeOfProperty != null) { return typeOfProperty.get(); } } final PsiPolyVariantReference reference = getReference(PyResolveContext.noImplicits().withTypeEvalContext(context)); final List<PsiElement> targets = PyUtil.multiResolveTopPriority(reference); if (targets.isEmpty()) { return getQualifiedReferenceTypeByControlFlow(context); } final List<PyType> members = new ArrayList<PyType>(); for (PsiElement target : targets) { if (target == this || target == null) { continue; } if (!target.isValid()) { LOG.error( "Reference " + this + " resolved to invalid element " + target + " (text=" + target.getText() + ")"); continue; } members.add(getTypeFromTarget(target, context, this)); } return PyUnionType.union(members); } finally { TypeEvalStack.evaluated(this); } }
public PyInplaceVariableIntroducer( PyTargetExpression target, IntroduceOperation operation, List<PsiElement> occurrences) { super( target, operation.getEditor(), operation.getProject(), "Introduce Variable", occurrences.toArray(new PsiElement[occurrences.size()]), null); myTarget = target; }
@NotNull public QualifiedResolveResult followAssignmentsChain(PyResolveContext resolveContext) { PyReferenceExpression seeker = this; QualifiedResolveResult ret = null; List<PyExpression> qualifiers = new ArrayList<PyExpression>(); PyExpression qualifier = seeker.getQualifier(); if (qualifier != null) { qualifiers.add(qualifier); } Set<PsiElement> visited = new HashSet<PsiElement>(); visited.add(this); SEARCH: while (ret == null) { ResolveResult[] targets = seeker.getReference(resolveContext).multiResolve(false); for (ResolveResult target : targets) { PsiElement elt = target.getElement(); if (elt instanceof PyTargetExpression) { PsiElement assigned_from = null; final PyTargetExpression expr = (PyTargetExpression) elt; final TypeEvalContext context = resolveContext.getTypeEvalContext(); if (context.maySwitchToAST(expr) || expr.getStub() == null) { assigned_from = expr.findAssignedValue(); } // TODO: Maybe findAssignedValueByStub() should become a part of the PyTargetExpression // interface else if (elt instanceof PyTargetExpressionImpl) { assigned_from = ((PyTargetExpressionImpl) elt).findAssignedValueByStub(context); } if (assigned_from instanceof PyReferenceExpression) { if (visited.contains(assigned_from)) { break; } visited.add(assigned_from); seeker = (PyReferenceExpression) assigned_from; if (seeker.getQualifier() != null) { qualifiers.add(seeker.getQualifier()); } continue SEARCH; } else if (assigned_from != null) ret = new QualifiedResolveResultImpl(assigned_from, qualifiers, false); } else if (ret == null && elt instanceof PyElement && target.isValidResult()) { // remember this result, but a further reference may be the next resolve result ret = new QualifiedResolveResultImpl( elt, qualifiers, target instanceof ImplicitResolveResult); } } // all resolve results checked, reassignment not detected, nothing more to do break; } if (ret == null) ret = EMPTY_RESULT; return ret; }
@NotNull private static List<PyType> getIndexTypes( @NotNull PySubscriptionExpression expression, @NotNull Context context) { final List<PyType> types = new ArrayList<>(); final PyExpression indexExpr = expression.getIndexExpression(); if (indexExpr instanceof PyTupleExpression) { final PyTupleExpression tupleExpr = (PyTupleExpression) indexExpr; for (PyExpression expr : tupleExpr.getElements()) { types.add(getType(expr, context)); } } else if (indexExpr != null) { types.add(getType(indexExpr, context)); } return types; }
@Nullable private static PyType getGenericConstructorType( @NotNull PyFunction function, @NotNull Context context) { if (PyUtil.isInit(function)) { final PyClass cls = function.getContainingClass(); if (cls != null) { final List<PyGenericType> genericTypes = collectGenericTypes(cls, context); final List<PyType> elementTypes = new ArrayList<>(genericTypes); if (!elementTypes.isEmpty()) { return new PyCollectionTypeImpl(cls, false, elementTypes); } } } return null; }
@Nullable protected static PsiElement findOccurrenceUnderCaret( List<PsiElement> occurrences, Editor editor) { if (occurrences.isEmpty()) { return null; } int offset = editor.getCaretModel().getOffset(); for (PsiElement occurrence : occurrences) { if (occurrence != null && occurrence.getTextRange().contains(offset)) { return occurrence; } } int line = editor.getDocument().getLineNumber(offset); for (PsiElement occurrence : occurrences) { if (occurrence.isValid() && editor.getDocument().getLineNumber(occurrence.getTextRange().getStartOffset()) == line) { return occurrence; } } for (PsiElement occurrence : occurrences) { if (occurrence.isValid()) { return occurrence; } } return null; }
@Nullable public Ref<PyType> getParameterType( @NotNull PyNamedParameter param, @NotNull PyFunction func, @NotNull TypeEvalContext context) { final PyAnnotation annotation = param.getAnnotation(); if (annotation != null) { // XXX: Requires switching from stub to AST final PyExpression value = annotation.getValue(); if (value != null) { final PyType type = getType(value, new Context(context)); if (type != null) { final PyType optionalType = getOptionalTypeFromDefaultNone(param, type, context); return Ref.create(optionalType != null ? optionalType : type); } } } final String paramComment = param.getTypeCommentAnnotation(); if (paramComment != null) { return Ref.create(getStringBasedType(paramComment, param, new Context(context))); } final String comment = func.getTypeCommentAnnotation(); if (comment != null) { final PyTypeParser.ParseResult result = PyTypeParser.parsePep484FunctionTypeComment(param, comment); final PyCallableType functionType = as(result.getType(), PyCallableType.class); if (functionType != null) { final List<PyCallableParameter> paramTypes = functionType.getParameters(context); // Function annotation of kind (...) -> Type if (paramTypes == null) { return Ref.create(); } final PyParameter[] funcParams = func.getParameterList().getParameters(); final int startOffset = omitFirstParamInTypeComment(func) ? 1 : 0; for (int paramIndex = 0; paramIndex < funcParams.length; paramIndex++) { if (funcParams[paramIndex] == param) { final int typeIndex = paramIndex - startOffset; if (typeIndex >= 0 && typeIndex < paramTypes.size()) { return Ref.create(paramTypes.get(typeIndex).getType(context)); } break; } } } } return null; }
/** * Moves methods (as opposite to {@link #makeMethodsAbstract(java.util.Collection, * com.jetbrains.python.psi.PyClass...)}) * * @param from source * @param methodsToMove what to move * @param to where * @return newly added methods */ private static List<PyElement> moveMethods( final PyClass from, final Collection<PyFunction> methodsToMove, final PyClass... to) { final List<PyElement> result = new ArrayList<PyElement>(); for (final PyClass destClass : to) { // We move copies here because there may be several destinations final List<PyFunction> copies = new ArrayList<PyFunction>(methodsToMove.size()); for (final PyFunction element : methodsToMove) { final PyFunction newMethod = (PyFunction) element.copy(); copies.add(newMethod); } result.addAll(PyClassRefactoringUtil.copyMethods(copies, destClass)); } deleteElements(methodsToMove); PyClassRefactoringUtil.insertPassIfNeeded(from); return result; }
private boolean smartIntroduce(final IntroduceOperation operation) { final Editor editor = operation.getEditor(); final PsiFile file = operation.getFile(); int offset = editor.getCaretModel().getOffset(); PsiElement elementAtCaret = file.findElementAt(offset); if (!checkIntroduceContext(file, editor, elementAtCaret)) return true; final List<PyExpression> expressions = new ArrayList<PyExpression>(); while (elementAtCaret != null) { if (elementAtCaret instanceof PyStatement || elementAtCaret instanceof PyFile) { break; } if (elementAtCaret instanceof PyExpression && isValidIntroduceVariant(elementAtCaret)) { expressions.add((PyExpression) elementAtCaret); } elementAtCaret = elementAtCaret.getParent(); } if (expressions.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { operation.setElement(expressions.get(0)); performActionOnElement(operation); return true; } else if (expressions.size() > 1) { IntroduceTargetChooser.showChooser( editor, expressions, new Pass<PyExpression>() { @Override public void pass(PyExpression pyExpression) { operation.setElement(pyExpression); performActionOnElement(operation); } }, new Function<PyExpression, String>() { public String fun(PyExpression pyExpression) { return pyExpression.getText(); } }); return true; } return false; }
private static PyType getTypeByControlFlow( @NotNull String name, @NotNull TypeEvalContext context, @NotNull PyExpression anchor, @NotNull ScopeOwner scopeOwner) { PyAugAssignmentStatement augAssignment = PsiTreeUtil.getParentOfType(anchor, PyAugAssignmentStatement.class); try { final PyElement element = augAssignment != null ? augAssignment : anchor; final List<ReadWriteInstruction> defs = PyDefUseUtil.getLatestDefs(scopeOwner, name, element, true); if (!defs.isEmpty()) { PyType type = defs.get(0).getType(context, anchor); for (int i = 1; i < defs.size(); i++) { type = PyUnionType.union(type, defs.get(i).getType(context, anchor)); } return type; } } catch (PyDefUseUtil.InstructionNotFoundException ignored) { } return null; }
/** @param self should be this */ @NotNull private static List<PyAssignmentStatement> findAttributesStatic(@NotNull final PsiElement self) { final List<PyAssignmentStatement> result = new ArrayList<>(); for (final PyAssignmentStatement statement : new PsiQuery(self).siblings(PyAssignmentStatement.class).getElements()) { for (final PyQualifiedExpression targetExpression : new PsiQuery(statement.getTargets()).filter(PyQualifiedExpression.class).getElements()) { final PyExpression qualifier = targetExpression.getQualifier(); if (qualifier == null) { continue; } final PsiReference qualifierReference = qualifier.getReference(); if (qualifierReference == null) { continue; } if (qualifierReference.isReferenceTo(self)) { result.add(statement); } } } return result; }
@Nullable private Modifier getWrappersFromStub() { final StubElement parentStub = getStub().getParentStub(); final List childrenStubs = parentStub.getChildrenStubs(); int index = childrenStubs.indexOf(getStub()); if (index >= 0 && index < childrenStubs.size() - 1) { StubElement nextStub = (StubElement) childrenStubs.get(index + 1); if (nextStub instanceof PyTargetExpressionStub) { final PyTargetExpressionStub targetExpressionStub = (PyTargetExpressionStub) nextStub; if (targetExpressionStub.getInitializerType() == PyTargetExpressionStub.InitializerType.CallExpression) { final QualifiedName qualifiedName = targetExpressionStub.getInitializer(); if (QualifiedName.fromComponents(PyNames.CLASSMETHOD).equals(qualifiedName)) { return CLASSMETHOD; } if (QualifiedName.fromComponents(PyNames.STATICMETHOD).equals(qualifiedName)) { return STATICMETHOD; } } } } return null; }
@NotNull private static List<PsiElement> tryResolving( @NotNull PyExpression expression, @NotNull TypeEvalContext context) { final List<PsiElement> elements = Lists.newArrayList(); if (expression instanceof PyReferenceExpression) { final PyReferenceExpression referenceExpr = (PyReferenceExpression) expression; final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); final PsiPolyVariantReference reference = referenceExpr.getReference(resolveContext); final List<PsiElement> resolved = PyUtil.multiResolveTopPriority(reference); for (PsiElement element : resolved) { if (element instanceof PyFunction) { final PyFunction function = (PyFunction) element; if (PyUtil.isInit(function)) { final PyClass cls = function.getContainingClass(); if (cls != null) { elements.add(cls); continue; } } } else if (element instanceof PyTargetExpression) { final PyTargetExpression targetExpr = (PyTargetExpression) element; // XXX: Requires switching from stub to AST final PyExpression assignedValue = targetExpr.findAssignedValue(); if (assignedValue != null) { elements.add(assignedValue); continue; } } if (element != null) { elements.add(element); } } } return !elements.isEmpty() ? elements : Collections.singletonList(expression); }
protected static PsiElement findAnchor(List<PsiElement> occurrences) { PsiElement anchor = occurrences.get(0); next: do { final PyStatement statement = PsiTreeUtil.getParentOfType(anchor, PyStatement.class); if (statement != null) { final PsiElement parent = statement.getParent(); for (PsiElement element : occurrences) { if (!PsiTreeUtil.isAncestor(parent, element, true)) { anchor = statement; continue next; } } } return statement; } while (true); }
@Nullable private String extractReturnType() { final String ARROW = "->"; final StructuredDocString structuredDocString = getStructuredDocString(); if (structuredDocString != null) { return structuredDocString.getReturnType(); } final String docString = getDocStringValue(); if (docString != null && docString.contains(ARROW)) { final List<String> lines = StringUtil.split(docString, "\n"); while (lines.size() > 0 && lines.get(0).trim().length() == 0) { lines.remove(0); } if (lines.size() > 1 && lines.get(1).trim().length() == 0) { String firstLine = lines.get(0); int pos = firstLine.lastIndexOf(ARROW); if (pos >= 0) { return firstLine.substring(pos + 2).trim(); } } } return null; }
/** @param anchor should never be null or null will be returned */ @NotNull public static ParseResult parse(@Nullable final PsiElement anchor, @NotNull String type) { PyPsiUtils.assertValid(anchor); if (anchor == null) { return EMPTY_RESULT; } final ForwardDeclaration<ParseResult, PyElementType> typeExpr = ForwardDeclaration.create(); 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> tupleType = op("(") .skipThen(typeExpr) .then(many(op(",").skipThen(typeExpr))) .thenSkip(op(")")) .map( value -> { ParseResult result = value.getFirst(); final List<ParseResult> rest = value.getSecond(); if (rest.isEmpty()) { return result; } final List<PyType> types = new ArrayList<>(); types.add(result.getType()); for (ParseResult r : rest) { result = result.merge(r); types.add(r.getType()); } return result.withType(PyTupleType.create(anchor, types)); }) .named("tuple-type"); final FunctionalParser<ParseResult, PyElementType> typeParameter = token(PARAMETER) .then(maybe(op("<=").skipThen(typeExpr))) .map( value -> { final Token<PyElementType> token = value.getFirst(); final String name = token.getText().toString(); final TextRange range = token.getRange(); final ParseResult boundResult = value.getSecond(); if (boundResult != null) { final PyGenericType type1 = new PyGenericType(name, boundResult.getType()); final ParseResult result = new ParseResult(null, type1, range); return result.merge(boundResult).withType(type1); } return new ParseResult(null, new PyGenericType(name, null), range); }) .named("type-parameter"); final FunctionalParser<ParseResult, PyElementType> simpleExpr = classType.or(tupleType).or(typeParameter).named("simple-expr"); final FunctionalParser<ParseResult, PyElementType> paramExpr = classType .thenSkip(op("[")) .then(typeExpr) .then(many(op(",").skipThen(typeExpr))) .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 PyType firstType = first.getType(); 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); } } if (firstType instanceof PyClassType) { final PyType type1 = new PyCollectionTypeImpl( ((PyClassType) firstType).getPyClass(), false, elementTypes); return result.withType(type1); } return EMPTY_RESULT; }) .or( classType .thenSkip(op("of")) .then(simpleExpr) .map( value -> { final ParseResult firstResult = value.getFirst(); final ParseResult secondResult = value.getSecond(); final ParseResult result = firstResult.merge(secondResult); final PyType firstType = firstResult.getType(); final PyType secondType = secondResult.getType(); if (firstType != null) { if (firstType instanceof PyClassType && secondType != null) { return result.withType( new PyCollectionTypeImpl( ((PyClassType) firstType).getPyClass(), false, Collections.singletonList(secondType))); } return result.withType(firstType); } return EMPTY_RESULT; })) .or( classType .thenSkip(op("from")) .then(simpleExpr) .thenSkip(op("to")) .then(simpleExpr) .map( value -> { final Pair<ParseResult, ParseResult> firstPair = value.getFirst(); final ParseResult first = firstPair.getFirst(); final ParseResult second = firstPair.getSecond(); final ParseResult third = value.getSecond(); final PyType firstType = first.getType(); if (firstType instanceof PyClassType) { final List<PyType> elementTypes = Arrays.asList(second.getType(), third.getType()); final PyCollectionTypeImpl type1 = new PyCollectionTypeImpl( ((PyClassType) firstType).getPyClass(), false, elementTypes); return first.merge(second).merge(third).withType(type1); } return EMPTY_RESULT; })) .named("param-expr"); final FunctionalParser<ParseResult, PyElementType> callableExpr = op("(") .skipThen(maybe(typeExpr.then(many(op(",").skipThen(typeExpr))))) .thenSkip(op(")")) .thenSkip(op("->")) .then(typeExpr) .map( value -> { final List<PyCallableParameter> parameters = new ArrayList<>(); final ParseResult returnResult = value.getSecond(); ParseResult result; final Pair<ParseResult, List<ParseResult>> firstPair = value.getFirst(); if (firstPair != null) { final ParseResult first = firstPair.getFirst(); final List<ParseResult> second = firstPair.getSecond(); result = first; parameters.add(new PyCallableParameterImpl(null, first.getType())); for (ParseResult r : second) { result = result.merge(r); parameters.add(new PyCallableParameterImpl(null, r.getType())); } result = result.merge(returnResult); } else { result = returnResult; } return result.withType( new PyCallableTypeImpl(parameters, returnResult.getType())); }) .named("callable-expr"); final FunctionalParser<ParseResult, PyElementType> singleExpr = paramExpr.or(callableExpr).or(simpleExpr).named("single-expr"); final FunctionalParser<ParseResult, PyElementType> unionExpr = singleExpr .then(many(op("or").or(op("|")).skipThen(singleExpr))) .map( value -> { final ParseResult first = value.getFirst(); final List<ParseResult> rest = value.getSecond(); if (rest.isEmpty()) { return first; } final List<PyType> types = new ArrayList<>(); types.add(first.getType()); ParseResult result = first; for (ParseResult r : rest) { types.add(r.getType()); result = result.merge(r); } return result.withType(PyUnionType.union(types)); }) .named("union-expr"); typeExpr.define(unionExpr).named("type-expr"); final FunctionalParser<ParseResult, PyElementType> typeFile = typeExpr.endOfInput().named("type-file"); try { return typeFile.parse(tokenize(type)); } catch (ParserException e) { return EMPTY_RESULT; } }
@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; } }
@Nullable @Override public ParseResult fun(@NotNull Pair<Token<PyElementType>, List<Token<PyElementType>>> value) { final Token<PyElementType> first = value.getFirst(); final List<Token<PyElementType>> rest = value.getSecond(); final TextRange firstRange = first.getRange(); final boolean unqualified = rest.isEmpty(); if (unqualified) { final ParseResult result = parseBuiltinType(first); if (result != null) { return result; } } final PsiFile file = myAnchor.getContainingFile(); final List<Token<PyElementType>> tokens = new ArrayList<>(); tokens.add(first); tokens.addAll(rest); if (file instanceof PyFile) { final PyFile pyFile = (PyFile) file; final TypeEvalContext context = TypeEvalContext.codeInsightFallback(file.getProject()); final Map<TextRange, PyType> types = new HashMap<>(); final Map<PyType, TextRange> fullRanges = new HashMap<>(); final Map<PyType, PyImportElement> imports = new HashMap<>(); PyType type = resolveQualifierType(tokens, pyFile, context, types, fullRanges, imports); PsiElement resolved = type != null ? getElement(type) : null; if (type != null) { final PyResolveContext resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(context); final PyExpression expression = myAnchor instanceof PyExpression ? (PyExpression) myAnchor : null; for (Token<PyElementType> token : tokens) { final PyType qualifierType = type; type = null; final List<? extends RatedResolveResult> results = qualifierType.resolveMember( token.getText().toString(), expression, AccessDirection.READ, resolveContext); if (results != null && !results.isEmpty()) { resolved = results.get(0).getElement(); if (resolved instanceof PyTypedElement) { type = context.getType((PyTypedElement) resolved); if (type != null && !allowResolveToType(type)) { type = null; break; } if (type instanceof PyClassLikeType) { type = ((PyClassLikeType) type).toInstance(); } } } if (type == null) { break; } types.put(token.getRange(), type); fullRanges.put( type, TextRange.create(firstRange.getStartOffset(), token.getRange().getEndOffset())); } if (type != null) { return new ParseResult(resolved, type, types, fullRanges, imports); } } } return EMPTY_RESULT; }