public static int getLambdaIdx(PsiExpressionList expressionList, final PsiElement element) { PsiExpression[] expressions = expressionList.getExpressions(); for (int i = 0; i < expressions.length; i++) { PsiExpression expression = expressions[i]; if (PsiTreeUtil.isAncestor(expression, element, false)) { return i; } } return -1; }
@Nullable private static InferenceSession startTopLevelInference( final PsiCall topLevelCall, final ParameterTypeInferencePolicy policy) { final JavaResolveResult result = topLevelCall.resolveMethodGenerics(); if (result instanceof MethodCandidateInfo) { final PsiMethod method = ((MethodCandidateInfo) result).getElement(); final PsiParameter[] topLevelParameters = method.getParameterList().getParameters(); final PsiExpressionList topLevelCallArgumentList = topLevelCall.getArgumentList(); LOG.assertTrue(topLevelCallArgumentList != null, topLevelCall); final PsiExpression[] topLevelArguments = topLevelCallArgumentList.getExpressions(); return PsiResolveHelper.ourGraphGuard.doPreventingRecursion( topLevelCall, true, new Computable<InferenceSession>() { @Override public InferenceSession compute() { final InferenceSession topLevelSession = new InferenceSession( method.getTypeParameters(), ((MethodCandidateInfo) result).getSiteSubstitutor(), topLevelCall.getManager(), topLevelCall, policy); topLevelSession.initExpressionConstraints( topLevelParameters, topLevelArguments, topLevelCall, method, ((MethodCandidateInfo) result).isVarargs()); topLevelSession.infer( topLevelParameters, topLevelArguments, topLevelCall, ((MethodCandidateInfo) result).createProperties()); return topLevelSession; } }); } return null; }
@Nullable private static PsiExpression createActualArgument( JavaChangeInfo changeInfo, final PsiExpressionList list, final JavaParameterInfo info, final boolean toInsertDefaultValue, final PsiExpression[] args) throws IncorrectOperationException { final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory(); final int index = info.getOldIndex(); if (index >= 0) { return args[index]; } else { if (toInsertDefaultValue) { return createDefaultValue(changeInfo, factory, info, list); } else { return factory.createExpressionFromText(info.getName(), list); } } }
private NamesByExprInfo suggestVariableNameByExpressionPlace( PsiExpression expr, final VariableKind variableKind, boolean correctKeywords) { if (expr.getParent() instanceof PsiExpressionList) { PsiExpressionList list = (PsiExpressionList) expr.getParent(); PsiElement listParent = list.getParent(); PsiSubstitutor subst = PsiSubstitutor.EMPTY; PsiMethod method = null; if (listParent instanceof PsiMethodCallExpression) { final JavaResolveResult resolveResult = ((PsiMethodCallExpression) listParent).getMethodExpression().advancedResolve(false); method = (PsiMethod) resolveResult.getElement(); subst = resolveResult.getSubstitutor(); } else { if (listParent instanceof PsiAnonymousClass) { listParent = listParent.getParent(); } if (listParent instanceof PsiNewExpression) { method = ((PsiNewExpression) listParent).resolveConstructor(); } } if (method != null) { final PsiElement navElement = method.getNavigationElement(); if (navElement instanceof PsiMethod) { method = (PsiMethod) navElement; } PsiExpression[] expressions = list.getExpressions(); int index = -1; for (int i = 0; i < expressions.length; i++) { if (expressions[i] == expr) { index = i; break; } } PsiParameter[] parameters = method.getParameterList().getParameters(); if (index < parameters.length) { String name = parameters[index].getName(); if (name != null && TypeConversionUtil.areTypesAssignmentCompatible( subst.substitute(parameters[index].getType()), expr)) { name = variableNameToPropertyName(name, VariableKind.PARAMETER); String[] names = getSuggestionsByName(name, variableKind, false, correctKeywords); if (expressions.length == 1) { final String methodName = method.getName(); String[] words = NameUtil.nameToWords(methodName); if (words.length > 0) { final String firstWord = words[0]; if (SET_PREFIX.equals(firstWord)) { final String propertyName = methodName.substring(firstWord.length()); final String[] setterNames = getSuggestionsByName(propertyName, variableKind, false, correctKeywords); names = ArrayUtil.mergeArrays(names, setterNames); } } } return new NamesByExprInfo(name, names); } } } } else if (expr.getParent() instanceof PsiAssignmentExpression && variableKind == VariableKind.PARAMETER) { final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression) expr.getParent(); if (expr == assignmentExpression.getRExpression()) { final PsiExpression leftExpression = assignmentExpression.getLExpression(); if (leftExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression) leftExpression).getQualifier() == null) { String name = leftExpression.getText(); if (name != null) { final PsiElement resolve = ((PsiReferenceExpression) leftExpression).resolve(); if (resolve instanceof PsiVariable) { name = variableNameToPropertyName(name, getVariableKind((PsiVariable) resolve)); } String[] names = getSuggestionsByName(name, variableKind, false, correctKeywords); return new NamesByExprInfo(name, names); } } } } return new NamesByExprInfo(null, ArrayUtil.EMPTY_STRING_ARRAY); }
@Nullable private static PsiExpression createDefaultValue( JavaChangeInfo changeInfo, final PsiElementFactory factory, final JavaParameterInfo info, final PsiExpressionList list) throws IncorrectOperationException { if (info.isUseAnySingleVariable()) { final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(list.getProject()).getResolveHelper(); final PsiType type = info.getTypeWrapper().getType(changeInfo.getMethod(), list.getManager()); final VariablesProcessor processor = new VariablesProcessor(false) { protected boolean check(PsiVariable var, ResolveState state) { if (var instanceof PsiField && !resolveHelper.isAccessible((PsiField) var, list, null)) return false; if (var instanceof PsiLocalVariable && list.getTextRange().getStartOffset() <= var.getTextRange().getStartOffset()) return false; if (PsiTreeUtil.isAncestor(var, list, false)) return false; final PsiType varType = state.get(PsiSubstitutor.KEY).substitute(var.getType()); return type.isAssignableFrom(varType); } public boolean execute(PsiElement pe, ResolveState state) { super.execute(pe, state); return size() < 2; } }; PsiScopesUtil.treeWalkUp(processor, list, null); if (processor.size() == 1) { final PsiVariable result = processor.getResult(0); return factory.createExpressionFromText(result.getName(), list); } if (processor.size() == 0) { final PsiClass parentClass = PsiTreeUtil.getParentOfType(list, PsiClass.class); if (parentClass != null) { PsiClass containingClass = parentClass; final Set<PsiClass> containingClasses = new HashSet<PsiClass>(); while (containingClass != null) { if (type.isAssignableFrom(factory.createType(containingClass, PsiSubstitutor.EMPTY))) { containingClasses.add(containingClass); } containingClass = PsiTreeUtil.getParentOfType(containingClass, PsiClass.class); } if (containingClasses.size() == 1) { return RefactoringUtil.createThisExpression( parentClass.getManager(), containingClasses.contains(parentClass) ? null : containingClasses.iterator().next()); } } } } final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(list, PsiCallExpression.class); final String defaultValue = info.getDefaultValue(); return callExpression != null ? info.getValue(callExpression) : defaultValue.length() > 0 ? factory.createExpressionFromText(defaultValue, list) : null; }
// This methods works equally well for primary usages as well as for propagated callers' usages private static void fixActualArgumentsList( PsiExpressionList list, JavaChangeInfo changeInfo, boolean toInsertDefaultValue, PsiSubstitutor substitutor) throws IncorrectOperationException { final PsiElementFactory factory = JavaPsiFacade.getInstance(list.getProject()).getElementFactory(); if (changeInfo.isParameterSetOrOrderChanged()) { if (changeInfo instanceof JavaChangeInfoImpl && ((JavaChangeInfoImpl) changeInfo).isPropagationEnabled) { final ParameterInfoImpl[] createdParmsInfo = ((JavaChangeInfoImpl) changeInfo).getCreatedParmsInfoWithoutVarargs(); for (ParameterInfoImpl info : createdParmsInfo) { PsiExpression newArg; if (toInsertDefaultValue) { newArg = createDefaultValue(changeInfo, factory, info, list); } else { newArg = factory.createExpressionFromText(info.getName(), list); } JavaCodeStyleManager.getInstance(list.getProject()) .shortenClassReferences(list.add(newArg)); } } else { final PsiExpression[] args = list.getExpressions(); final int nonVarargCount = getNonVarargCount(changeInfo, args); final int varargCount = args.length - nonVarargCount; if (varargCount < 0) return; PsiExpression[] newVarargInitializers = null; final int newArgsLength; final int newNonVarargCount; final JavaParameterInfo[] newParms = changeInfo.getNewParameters(); if (changeInfo.isArrayToVarargs()) { newNonVarargCount = newParms.length - 1; final JavaParameterInfo lastNewParm = newParms[newParms.length - 1]; final PsiExpression arrayToConvert = args[lastNewParm.getOldIndex()]; if (arrayToConvert instanceof PsiNewExpression) { final PsiNewExpression expression = (PsiNewExpression) arrayToConvert; final PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer(); if (arrayInitializer != null) { newVarargInitializers = arrayInitializer.getInitializers(); } } newArgsLength = newVarargInitializers == null ? newParms.length : newNonVarargCount + newVarargInitializers.length; } else if (changeInfo.isRetainsVarargs()) { newNonVarargCount = newParms.length - 1; newArgsLength = newNonVarargCount + varargCount; } else if (changeInfo.isObtainsVarags()) { newNonVarargCount = newParms.length - 1; newArgsLength = newNonVarargCount; } else { newNonVarargCount = newParms.length; newArgsLength = newParms.length; } String[] oldVarargs = null; if (changeInfo.wasVararg() && !changeInfo.isRetainsVarargs()) { oldVarargs = new String[varargCount]; for (int i = nonVarargCount; i < args.length; i++) { oldVarargs[i - nonVarargCount] = args[i].getText(); } } final PsiExpression[] newArgs = new PsiExpression[newArgsLength]; for (int i = 0; i < newNonVarargCount; i++) { if (newParms[i].getOldIndex() == nonVarargCount && oldVarargs != null) { PsiType type = newParms[i].createType(changeInfo.getMethod(), list.getManager()); if (type instanceof PsiArrayType) { type = substitutor.substitute(type); type = TypeConversionUtil.erasure(type); String typeText = type.getCanonicalText(); if (type instanceof PsiEllipsisType) { typeText = typeText.replace("...", "[]"); } String text = "new " + typeText + "{" + StringUtil.join(oldVarargs, ",") + "}"; newArgs[i] = factory.createExpressionFromText(text, changeInfo.getMethod()); continue; } } newArgs[i] = createActualArgument(changeInfo, list, newParms[i], toInsertDefaultValue, args); } if (changeInfo.isArrayToVarargs()) { if (newVarargInitializers == null) { newArgs[newNonVarargCount] = createActualArgument( changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args); } else { System.arraycopy( newVarargInitializers, 0, newArgs, newNonVarargCount, newVarargInitializers.length); } } else { final int newVarargCount = newArgsLength - newNonVarargCount; LOG.assertTrue(newVarargCount == 0 || newVarargCount == varargCount); for (int i = newNonVarargCount; i < newArgsLength; i++) { final int oldIndex = newParms[newNonVarargCount].getOldIndex(); if (oldIndex >= 0 && oldIndex != nonVarargCount) { newArgs[i] = createActualArgument( changeInfo, list, newParms[newNonVarargCount], toInsertDefaultValue, args); } else { System.arraycopy(args, nonVarargCount, newArgs, newNonVarargCount, newVarargCount); break; } } } ChangeSignatureUtil.synchronizeList( list, Arrays.asList(newArgs), ExpressionList.INSTANCE, changeInfo.toRemoveParm()); } } }
public List<PsiExpression> getChildren(PsiExpressionList psiExpressionList) { return Arrays.asList(psiExpressionList.getExpressions()); }
@Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiForeachStatement foreachStatement = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiForeachStatement.class); if (foreachStatement != null) { if (!FileModificationService.getInstance().preparePsiElementForWrite(foreachStatement)) return; final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); PsiStatement body = foreachStatement.getBody(); final PsiExpression iteratedValue = foreachStatement.getIteratedValue(); if (body != null && iteratedValue != null) { final PsiParameter parameter = foreachStatement.getIterationParameter(); final PsiIfStatement ifStatement = extractIfStatement(body); final PsiMethodCallExpression methodCallExpression = extractAddCall(body, ifStatement); if (methodCallExpression == null) return; if (isAddAllCall(foreachStatement, body)) { restoreComments(foreachStatement, body); final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression(); final String qualifierText = qualifierExpression != null ? qualifierExpression.getText() : ""; final String callText = StringUtil.getQualifiedName( qualifierText, "addAll(" + getIteratedValueText(iteratedValue) + ");"); PsiElement result = foreachStatement.replace( elementFactory.createStatementFromText(callText, foreachStatement)); reformatWhenNeeded(project, result); return; } final StringBuilder builder = new StringBuilder(getIteratedValueText(iteratedValue) + ".stream()"); builder.append(createFiltersChainText(body, parameter, ifStatement)); builder.append( createMapperFunctionalExpressionText( parameter, methodCallExpression.getArgumentList().getExpressions()[0])); builder.append(".collect(java.util.stream.Collectors."); PsiElement result = null; try { final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression(); if (qualifierExpression instanceof PsiReferenceExpression) { final PsiElement resolve = ((PsiReferenceExpression) qualifierExpression).resolve(); if (resolve instanceof PsiVariable) { if (resolve instanceof PsiLocalVariable && foreachStatement.equals( PsiTreeUtil.skipSiblingsForward( resolve.getParent(), PsiWhiteSpace.class))) { final PsiExpression initializer = ((PsiVariable) resolve).getInitializer(); if (initializer instanceof PsiNewExpression) { final PsiExpressionList argumentList = ((PsiNewExpression) initializer).getArgumentList(); if (argumentList != null && argumentList.getExpressions().length == 0) { restoreComments(foreachStatement, body); final String callText = builder.toString() + createInitializerReplacementText( ((PsiVariable) resolve).getType(), initializer) + ")"; result = initializer.replace( elementFactory.createExpressionFromText(callText, null)); simplifyRedundantCast(result); foreachStatement.delete(); return; } } } } } restoreComments(foreachStatement, body); final String qualifierText = qualifierExpression != null ? qualifierExpression.getText() : ""; final String callText = StringUtil.getQualifiedName( qualifierText, "addAll(" + builder.toString() + "toList()));"); result = foreachStatement.replace( elementFactory.createStatementFromText(callText, foreachStatement)); simplifyRedundantCast(result); } finally { reformatWhenNeeded(project, result); } } } }
@Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiForeachStatement foreachStatement = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiForeachStatement.class); if (foreachStatement != null) { if (!FileModificationService.getInstance().preparePsiElementForWrite(foreachStatement)) return; PsiStatement body = foreachStatement.getBody(); final PsiExpression iteratedValue = foreachStatement.getIteratedValue(); if (body != null && iteratedValue != null) { restoreComments(foreachStatement, body); final PsiParameter parameter = foreachStatement.getIterationParameter(); final PsiIfStatement ifStmt = extractIfStatement(body); StringBuilder buffer = new StringBuilder(getIteratedValueText(iteratedValue)); if (ifStmt != null) { final PsiStatement thenBranch = ifStmt.getThenBranch(); LOG.assertTrue(thenBranch != null); buffer.append(".stream()"); buffer.append(createFiltersChainText(body, parameter, ifStmt)); body = thenBranch; } buffer.append(".").append(getForEachMethodName()).append("("); final String functionalExpressionText = createForEachFunctionalExpressionText(project, body, parameter); final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); PsiExpressionStatement callStatement = (PsiExpressionStatement) elementFactory.createStatementFromText( buffer.toString() + functionalExpressionText + ");", foreachStatement); callStatement = (PsiExpressionStatement) foreachStatement.replace(callStatement); final PsiExpressionList argumentList = ((PsiCallExpression) callStatement.getExpression()).getArgumentList(); LOG.assertTrue(argumentList != null, callStatement.getText()); final PsiExpression[] expressions = argumentList.getExpressions(); LOG.assertTrue(expressions.length == 1); if (expressions[0] instanceof PsiFunctionalExpression && ((PsiFunctionalExpression) expressions[0]).getFunctionalInterfaceType() == null) { callStatement = (PsiExpressionStatement) callStatement.replace( elementFactory.createStatementFromText( buffer.toString() + "(" + parameter.getText() + ") -> " + wrapInBlock(body) + ");", callStatement)); } simplifyRedundantCast(callStatement); CodeStyleManager.getInstance(project) .reformat( JavaCodeStyleManager.getInstance(project).shortenClassReferences(callStatement)); } } }
@Nullable public static PsiType getFunctionalInterfaceType( PsiElement expression, final boolean tryToSubstitute, int paramIdx) { PsiElement parent = expression.getParent(); PsiElement element = expression; while (parent instanceof PsiParenthesizedExpression || parent instanceof PsiConditionalExpression) { if (parent instanceof PsiConditionalExpression && ((PsiConditionalExpression) parent).getThenExpression() != element && ((PsiConditionalExpression) parent).getElseExpression() != element) break; element = parent; parent = parent.getParent(); } if (parent instanceof PsiArrayInitializerExpression) { final PsiType psiType = ((PsiArrayInitializerExpression) parent).getType(); if (psiType instanceof PsiArrayType) { return ((PsiArrayType) psiType).getComponentType(); } } else if (parent instanceof PsiTypeCastExpression) { final PsiType castType = ((PsiTypeCastExpression) parent).getType(); if (castType instanceof PsiIntersectionType) { for (PsiType conjunctType : ((PsiIntersectionType) castType).getConjuncts()) { if (getFunctionalInterfaceMethod(conjunctType) != null) return conjunctType; } } return castType; } else if (parent instanceof PsiVariable) { return ((PsiVariable) parent).getType(); } else if (parent instanceof PsiAssignmentExpression && expression instanceof PsiExpression && !PsiUtil.isOnAssignmentLeftHand((PsiExpression) expression)) { final PsiExpression lExpression = ((PsiAssignmentExpression) parent).getLExpression(); return lExpression.getType(); } else if (parent instanceof PsiExpressionList) { final PsiExpressionList expressionList = (PsiExpressionList) parent; final int lambdaIdx = getLambdaIdx(expressionList, expression); if (lambdaIdx > -1) { PsiType cachedType = null; final Pair<PsiMethod, PsiSubstitutor> method = MethodCandidateInfo.getCurrentMethod(parent); if (method != null) { final PsiParameter[] parameters = method.first.getParameterList().getParameters(); cachedType = lambdaIdx < parameters.length ? method.second.substitute( getNormalizedType( parameters[adjustLambdaIdx(lambdaIdx, method.first, parameters)])) : null; if (!tryToSubstitute) return cachedType; } PsiElement gParent = expressionList.getParent(); if (gParent instanceof PsiAnonymousClass) { gParent = gParent.getParent(); } if (gParent instanceof PsiCall) { final PsiCall contextCall = (PsiCall) gParent; final JavaResolveResult resolveResult = contextCall.resolveMethodGenerics(); final PsiElement resolve = resolveResult.getElement(); if (resolve instanceof PsiMethod) { final PsiParameter[] parameters = ((PsiMethod) resolve).getParameterList().getParameters(); final int finalLambdaIdx = adjustLambdaIdx(lambdaIdx, (PsiMethod) resolve, parameters); if (finalLambdaIdx < parameters.length) { if (!tryToSubstitute) return getNormalizedType(parameters[finalLambdaIdx]); if (cachedType != null && paramIdx > -1) { final PsiMethod interfaceMethod = getFunctionalInterfaceMethod(cachedType); if (interfaceMethod != null) { final PsiClassType.ClassResolveResult cachedResult = PsiUtil.resolveGenericsClassInType(cachedType); final PsiType interfaceMethodParameterType = interfaceMethod.getParameterList().getParameters()[paramIdx].getType(); if (!dependsOnTypeParams( cachedResult.getSubstitutor().substitute(interfaceMethodParameterType), cachedType, expression)) { return cachedType; } } } return PsiResolveHelper.ourGuard.doPreventingRecursion( expression, true, new Computable<PsiType>() { @Override public PsiType compute() { return resolveResult .getSubstitutor() .substitute(getNormalizedType(parameters[finalLambdaIdx])); } }); } } return null; } } } else if (parent instanceof PsiReturnStatement) { final PsiLambdaExpression gParent = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class); if (gParent != null) { return getFunctionalInterfaceTypeByContainingLambda(gParent); } else { final PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class); if (method != null) { return method.getReturnType(); } } } else if (parent instanceof PsiLambdaExpression) { return getFunctionalInterfaceTypeByContainingLambda((PsiLambdaExpression) parent); } return null; }