void (@NotNull final PyCallExpression callExpr, @NotNull final PyFunction function, @NotNull final List<String> ret, @NotNull final Set<PyCallable> visited, @NotNull final TypeEvalContext context) { if (visited.contains(function)) { return; } boolean needSelf = function.getContainingClass() != null && function.getModifier() != PyFunction.Modifier.STATICMETHOD; final KwArgParameterCollector collector = new KwArgParameterCollector(needSelf, ret); List<PyCallableParameter> parameters = function.getParameters(context); int indexOfPySlashParameter = getIndexOfPySlashParameter(parameters); StreamEx .of(parameters) .skip(indexOfPySlashParameter + 1) .map(PyCallableParameter::getParameter) .nonNull() .forEach(parameter -> parameter.accept(collector)); if (collector.hasKwArgs()) { for (PyKeywordArgumentProvider provider : PyKeywordArgumentProvider.EP_NAME.getExtensionList()) { ret.addAll(provider.getKeywordArguments(function, callExpr)); } KwArgFromStatementCallCollector fromStatementCallCollector = new KwArgFromStatementCallCollector(ret, collector.getKwArgs()); function.getStatementList().acceptChildren(fromStatementCallCollector); //if (collector.hasOnlySelfAndKwArgs()) { // nothing interesting besides self and **kwargs, let's look at superclass (PY-778) if (fromStatementCallCollector.isKwArgsTransit()) { final PyFunction superMethod = as(PySuperMethodsSearch.search(function, context).findFirst(), PyFunction.class); if (superMethod != null) { addKeywordArgumentVariantsForFunction(callExpr, superMethod, ret, visited, context); } } } } public static class KwArgParameterCollector extends PyElementVisitor { private int myCount; private final boolean myNeedSelf; private final List<? super String> myRet; private boolean myHasSelf = false; private boolean myHasKwArgs = false; private PyParameter kwArgsParam = null; public KwArgParameterCollector(boolean needSelf, List<? super String> ret) { myNeedSelf = needSelf; myRet = ret; } @Override public void visitPyParameter(@NotNull PyParameter par) { myCount++; if (myCount == 1 && myNeedSelf) { myHasSelf = true; return; } PyNamedParameter namedParam = par.getAsNamed(); if (namedParam != null) { if (!namedParam.isKeywordContainer() && !namedParam.isPositionalContainer()) { myRet.add(namedParam.getName()); } else if (namedParam.isKeywordContainer()) { myHasKwArgs = true; kwArgsParam = namedParam; } } } public PyParameter getKwArgs() { return kwArgsParam; } public boolean hasKwArgs() { return myHasKwArgs; } } public static class KwArgFromStatementCallCollector extends PyElementVisitor { private final List<String> myRet; private final PyParameter myKwArgs; private boolean kwArgsTransit = true; public KwArgFromStatementCallCollector(List<String> ret, @NotNull PyParameter kwArgs) { myRet = ret; this.myKwArgs = kwArgs; } @Override public void visitPyElement(@NotNull PyElement node) { node.acceptChildren(this); } @Override public void visitPySubscriptionExpression(@NotNull PySubscriptionExpression node) { String operandName = node.getOperand().getName(); processGet(operandName, node.getIndexExpression()); } @Override public void visitPyCallExpression(@NotNull PyCallExpression node) { if (node.isCalleeText("pop", "get", "getattr")) { PyReferenceExpression child = PsiTreeUtil.getChildOfType(node.getCallee(), PyReferenceExpression.class); if (child != null) { String operandName = child.getName(); if (node.getArguments().length > 0) { PyExpression argument = node.getArguments()[0]; processGet(operandName, argument); } } } else if (node.isCalleeText("__init__")) { kwArgsTransit = false; for (PyExpression e : node.getArguments()) { if (e instanceof PyStarArgument kw) { if (Objects.equals(myKwArgs.getName(), kw.getFirstChild().getNextSibling().getText())) { kwArgsTransit = true; break; } } } } super.visitPyCallExpression(node); } private void processGet(String operandName, PyExpression argument) { if (Objects.equals(myKwArgs.getName(), operandName) && argument instanceof PyStringLiteralExpression) { String name = ((PyStringLiteralExpression)argument).getStringValue(); if (PyNames.isIdentifier(name)) { myRet.add(name); } } } /** * is name of kwargs parameter the same as transmitted to __init__ call * */ public boolean isKwArgsTransit() { return kwArgsTransit; } } }