[java] add cast fix when multiple arguments require fix simultaneously (IDEA-271993)

GitOrigin-RevId: 2f6dc7ed7341c851b56a80a9b6404678ac0da0b9
This commit is contained in:
Anna Kozlova
2021-06-22 14:55:14 +02:00
committed by intellij-monorepo-bot
parent 612d647e64
commit f803857dc1
5 changed files with 88 additions and 25 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
@@ -62,18 +62,19 @@ public abstract class ArgumentFixerActionFactory {
try {
PsiType expectedTypeByParent = PsiTypesUtil.getExpectedTypeByParent(call);
for (int i = 0; i < expressions.length; i++) {
PsiExpression expression = expressions[i];
PsiType exprType = expression.getType();
Set<String> suggestedCasts = new HashSet<>();
// find to which type we can cast this param to get valid method call
for (CandidateInfo candidate : methodCandidates) {
PsiMethod method = (PsiMethod)candidate.getElement();
PsiSubstitutor substitutor = candidate.getSubstitutor();
Map<Integer, Set<String>> suggestedCasts = new HashMap<>();
// find to which type we can cast this param to get valid method call
for (CandidateInfo candidate : methodCandidates) {
PsiMethod method = (PsiMethod)candidate.getElement();
PsiSubstitutor substitutor = candidate.getSubstitutor();
Map<Integer, PsiType> potentialCasts = new HashMap<>();
for (int i = 0; i < expressions.length; i++) {
PsiExpression expression = expressions[i];
PsiType exprType = expression.getType();
PsiType originalParameterType = PsiTypesUtil.getParameterType(method.getParameterList().getParameters(), i, true);
PsiType parameterType = substitutor.substitute(originalParameterType);
if (!PsiTypesUtil.isDenotableType(parameterType, call)) continue;
if (suggestedCasts.contains(parameterType.getCanonicalText())) continue;
if (suggestedCasts.computeIfAbsent(i, __ -> new HashSet<>()).contains(parameterType.getCanonicalText())) continue;
if (TypeConversionUtil.isPrimitiveAndNotNull(exprType) && parameterType instanceof PsiClassType) {
PsiType unboxedParameterType = PsiPrimitiveType.getUnboxedType(parameterType);
if (unboxedParameterType != null) {
@@ -82,21 +83,28 @@ public abstract class ArgumentFixerActionFactory {
}
// strict compare since even widening cast may help
if (Comparing.equal(exprType, parameterType)) continue;
PsiCall newCall = LambdaUtil.copyTopLevelCall(call); //copy with expected type
potentialCasts.put(i, parameterType);
}
if (!potentialCasts.isEmpty()) {
PsiCall newCall = LambdaUtil.copyTopLevelCall(call);
if (newCall == null) continue;
PsiExpression modifiedExpression = getModifiedArgument(expression, parameterType);
if (modifiedExpression == null) continue;
PsiExpressionList argumentList = newCall.getArgumentList();
if (argumentList == null) continue;
argumentList.getExpressions()[i].replace(modifiedExpression);
JavaResolveResult resolveResult = newCall.resolveMethodGenerics();
if (resolveResult.getElement() != null && resolveResult.isValidResult()) {
if (expectedTypeByParent != null && newCall instanceof PsiCallExpression) {
PsiType type = ((PsiCallExpression)newCall).getType();
if (type != null && !TypeConversionUtil.isAssignable(expectedTypeByParent, type)) continue;
for (Map.Entry<Integer, PsiType> entry : potentialCasts.entrySet()) {
replaceWithCast(expressions, newCall, entry);
}
doCheckNewCall(expectedTypeByParent, newCall, () -> {
for (Iterator<Map.Entry<Integer, PsiType>> iterator = potentialCasts.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<Integer, PsiType> entry = iterator.next();
registerCastIntention(highlightInfo, fixRange, list, suggestedCasts, entry);
iterator.remove();
}
suggestedCasts.add(parameterType.getCanonicalText());
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, createFix(list, i, parameterType));
});
for (Map.Entry<Integer, PsiType> entry : potentialCasts.entrySet()) {
PsiCall callWithSingleCast = LambdaUtil.copyTopLevelCall(call);
if (callWithSingleCast == null || replaceWithCast(expressions, callWithSingleCast, entry)) continue;
doCheckNewCall(expectedTypeByParent, callWithSingleCast, () -> registerCastIntention(highlightInfo, fixRange, list, suggestedCasts, entry));
}
}
}
@@ -106,6 +114,37 @@ public abstract class ArgumentFixerActionFactory {
}
}
private static void doCheckNewCall(PsiType expectedTypeByParent, PsiCall callWithSingleCast, Runnable registerIntentions) {
JavaResolveResult resolveResult = callWithSingleCast.resolveMethodGenerics();
if (resolveResult.getElement() != null && resolveResult.isValidResult()) {
if (expectedTypeByParent != null && callWithSingleCast instanceof PsiCallExpression) {
PsiType type = ((PsiCallExpression)callWithSingleCast).getType();
if (type != null && !TypeConversionUtil.isAssignable(expectedTypeByParent, type)) return;
}
registerIntentions.run();
}
}
private void registerCastIntention(HighlightInfo highlightInfo,
TextRange fixRange,
PsiExpressionList list,
Map<Integer, Set<String>> suggestedCasts,
Map.Entry<Integer, PsiType> entry) {
suggestedCasts.get(entry.getKey()).add(entry.getValue().getCanonicalText());
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, createFix(list, entry.getKey(), entry.getValue()));
}
private boolean replaceWithCast(PsiExpression[] expressions, PsiCall newCall, Map.Entry<Integer, PsiType> entry) {
Integer i = entry.getKey();
PsiType parameterType = entry.getValue();
PsiExpression modifiedExpression = getModifiedArgument(expressions[i], parameterType);
if (modifiedExpression == null) return true;
PsiExpressionList argumentList = newCall.getArgumentList();
if (argumentList == null) return true;
argumentList.getExpressions()[i].replace(modifiedExpression);
return false;
}
public abstract boolean areTypesConvertible(@NotNull PsiType exprType, @NotNull PsiType parameterType, @NotNull PsiElement context);
public abstract IntentionAction createFix(PsiExpressionList list, int i, PsiType parameterType);

View File

@@ -0,0 +1,14 @@
// "Cast 1st parameter to 'char'" "true"
class a {
private void test() {}
private void test(int i) {}
private void test(String s) {}
private void test(Object o) {}
private void test(char c,char f) {}
private void test(int c, char f) {}
void f() {
test((char) 0, 0);
}
}

View File

@@ -0,0 +1,10 @@
// "Cast 1st parameter to 'java.lang.Throwable'" "true"
class a {
void f(Throwable a, Throwable b) {}
void g() {
Exception e=null;
Object o = null;
f((Throwable) e,o);
}
}

View File

@@ -1,4 +1,4 @@
// "Cast 1st parameter to 'char'" "false"
// "Cast 1st parameter to 'char'" "true"
class a {
private void test() {}
private void test(int i) {}

View File

@@ -1,4 +1,4 @@
// "Cast 1st parameter to 'java.lang.Throwable'" "false"
// "Cast 1st parameter to 'java.lang.Throwable'" "true"
class a {
void f(Throwable a, Throwable b) {}
void g() {