[java-intentions] IDEA-299075 More scenarios for parameter fixes

1. Surround with array initialization
2. Apply .toArray() conversion
3. Limited vararg support
4. Limited qualifier propagation

GitOrigin-RevId: f81f593502df317b555e816af20cdec2d04488fc
This commit is contained in:
Tagir Valeev
2022-08-03 14:28:35 +02:00
committed by intellij-monorepo-bot
parent 7f3050849a
commit c99da54bb4
10 changed files with 124 additions and 24 deletions

View File

@@ -48,19 +48,29 @@ class AdaptExpressionTypeFixUtil {
Map.Entry<PsiTypeParameter, PsiType> substitution = findDesiredSubstitution(expectedTypeByParent, actualType, methodType);
if (substitution == null) return;
PsiTypeParameter typeParameter = substitution.getKey();
if (!PsiTreeUtil.isAncestor(method, typeParameter, true)) return;
PsiType expectedTypeValue = substitution.getValue();
PsiParameter[] parameters = method.getParameterList().getParameters();
Set<PsiTypeParameter> set = Set.of(typeParameter);
if (!PsiTreeUtil.isAncestor(method, typeParameter, true)) {
registerPatchQualifierFixes(info, call, method, typeParameter, expectedTypeValue, parameters, set);
return;
}
PsiParameter parameter =
StreamEx.of(parameters).collect(MoreCollectors.onlyOne(p -> PsiTypesUtil.mentionsTypeParameters(p.getType(), set))).orElse(null);
if (parameter == null) return;
PsiExpression arg = PsiUtil.skipParenthesizedExprDown(MethodCallUtils.getArgumentForParameter(call, parameter));
if (arg == null) return;
PsiClassType parameterType = tryCast(parameter.getType(), PsiClassType.class);
if (parameterType == null) return;
if (parameterType.rawType().equalsToText(CommonClassNames.JAVA_LANG_CLASS) && typeParameter == getSoleTypeParameter(parameterType)) {
PsiType parameterType = parameter.getType();
if (parameterType instanceof PsiEllipsisType && MethodCallUtils.isVarArgCall(call)) {
// Replace vararg only if there's single value
if (call.getArgumentList().getExpressionCount() != parameters.length) return;
parameterType = ((PsiEllipsisType)parameterType).getComponentType();
}
if (parameterType instanceof PsiClassType &&
((PsiClassType)parameterType).rawType().equalsToText(CommonClassNames.JAVA_LANG_CLASS) &&
typeParameter == getSoleTypeParameter(parameterType)) {
if (expectedTypeValue instanceof PsiClassType && JavaGenericsUtil.isReifiableType(expectedTypeValue)) {
ReplaceExpressionAction fix = new ReplaceExpressionAction(
arg, ((PsiClassType)expectedTypeValue).rawType().getCanonicalText() + ".class",
@@ -69,13 +79,38 @@ class AdaptExpressionTypeFixUtil {
}
}
PsiType expectedArgType = PsiSubstitutor.EMPTY.put(typeParameter, expectedTypeValue).substitute(parameterType);
if (arg instanceof PsiLambdaExpression) {
registerLambdaReturnFixes(info, (PsiLambdaExpression)arg, parameterType, expectedArgType, typeParameter);
if (arg instanceof PsiLambdaExpression && parameterType instanceof PsiClassType) {
registerLambdaReturnFixes(info, (PsiLambdaExpression)arg, (PsiClassType)parameterType, expectedArgType, typeParameter);
return;
}
registerExpectedTypeFixes(info, arg, expectedArgType);
}
private static void registerPatchQualifierFixes(@NotNull HighlightInfo info,
@NotNull PsiMethodCallExpression call,
@NotNull PsiMethod method,
@NotNull PsiTypeParameter typeParameter,
@NotNull PsiType expectedTypeValue,
@NotNull PsiParameter @NotNull [] parameters,
@NotNull Set<PsiTypeParameter> set) {
PsiMethodCallExpression qualifierCall = MethodCallUtils.getQualifierMethodCall(call);
if (qualifierCall == null) return;
JavaResolveResult qualifierResolve = qualifierCall.resolveMethodGenerics();
PsiMethod qualifierMethod = tryCast(qualifierResolve.getElement(), PsiMethod.class);
if (qualifierMethod == null) return;
PsiClassType qualifierType = tryCast(qualifierMethod.getReturnType(), PsiClassType.class);
if (qualifierType == null) return;
if (ContainerUtil.exists(parameters, p -> PsiTypesUtil.mentionsTypeParameters(p.getType(), set))) return;
PsiClassType.ClassResolveResult classResolveResult = qualifierType.resolveGenerics();
PsiClass qualifierClass = classResolveResult.getElement();
if (qualifierClass == null) return;
PsiType expectedQualifierType = JavaPsiFacade.getElementFactory(method.getProject())
.createType(qualifierClass, classResolveResult.getSubstitutor().put(typeParameter, expectedTypeValue));
if (!expectedQualifierType.equals(qualifierCall.getType())) {
registerPatchParametersFixes(info, qualifierCall, qualifierMethod, expectedQualifierType);
}
}
private static void registerLambdaReturnFixes(@NotNull HighlightInfo info,
@NotNull PsiLambdaExpression arg,
@NotNull PsiClassType parameterType,
@@ -123,14 +158,22 @@ class AdaptExpressionTypeFixUtil {
QuickFixAction.registerQuickFixAction(info, new WrapWithAdapterMethodCallFix(expectedType, expression, role));
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapWithOptionalFix(expectedType, expression));
QuickFixAction.registerQuickFixAction(info, new WrapExpressionFix(expectedType, expression, role));
PsiType argType = expression.getType();
PsiType actualType = expression.getType();
if (expression instanceof PsiMethodCallExpression) {
JavaResolveResult result = ((PsiMethodCallExpression)expression).resolveMethodGenerics();
if (result instanceof MethodCandidateInfo) {
argType = ((MethodCandidateInfo)result).getSubstitutor(false).substitute(argType);
actualType = ((MethodCandidateInfo)result).getSubstitutor(false).substitute(actualType);
}
}
PsiType castToType = suggestCastTo(expectedType, argType);
if (expectedType instanceof PsiArrayType) {
PsiType erasedValueType = TypeConversionUtil.erasure(actualType);
if (erasedValueType != null &&
TypeConversionUtil.isAssignable(((PsiArrayType)expectedType).getComponentType(), erasedValueType)) {
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createSurroundWithArrayFix(null, expression));
}
}
HighlightFixUtil.registerCollectionToArrayFixAction(info, actualType, expectedType, expression);
PsiType castToType = suggestCastTo(expectedType, actualType);
if (castToType != null) {
QuickFixAction.registerQuickFixAction(info, new AddTypeCastFix(castToType, expression, role));
}
@@ -173,17 +216,19 @@ class AdaptExpressionTypeFixUtil {
if (expected instanceof PsiWildcardType && ((PsiWildcardType)expected).isExtends()) {
expected = ((PsiWildcardType)expected).getExtendsBound();
}
if (!(methodType instanceof PsiClassType) || !(expected instanceof PsiClassType) || !(actual instanceof PsiClassType)) return null;
if (!(methodType instanceof PsiClassType)) return null;
PsiClass methodClass = ((PsiClassType)methodType).resolve();
PsiClass expectedClass = ((PsiClassType)expected).resolve();
PsiClass actualClass = ((PsiClassType)actual).resolve();
if (methodClass == null || expectedClass == null || actualClass == null) return null;
if (methodClass == null) return null;
if (methodClass instanceof PsiTypeParameter) {
if (!expected.equals(actual)) {
if (!expected.equals(actual) && !(expected instanceof PsiPrimitiveType)) {
return Map.entry((PsiTypeParameter)methodClass, expected);
}
return null;
}
if (!(expected instanceof PsiClassType) || !(actual instanceof PsiClassType)) return null;
PsiClass expectedClass = ((PsiClassType)expected).resolve();
PsiClass actualClass = ((PsiClassType)actual).resolve();
if (expectedClass == null || actualClass == null) return null;
if (!expectedClass.isEquivalentTo(actualClass) || !expectedClass.isEquivalentTo(methodClass)) return null;
PsiType[] methodTypeParameters = ((PsiClassType)methodType).getParameters();
PsiType[] expectedTypeParameters = ((PsiClassType)expected).getParameters();

View File

@@ -569,7 +569,6 @@ public final class HighlightUtil {
}
if (expression != null) {
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(highlightInfo, expression, lType);
HighlightFixUtil.registerCollectionToArrayFixAction(highlightInfo, rType, lType, expression);
if (!(expression.getParent() instanceof PsiConditionalExpression && PsiType.VOID.equals(lType))) {
HighlightFixUtil.registerChangeReturnTypeFix(highlightInfo, expression, lType);
}
@@ -617,18 +616,10 @@ public final class HighlightUtil {
TextRange textRange = statement.getTextRange();
errorResult = checkAssignability(returnType, valueType, returnValue, textRange, returnValue.getStartOffsetInParent());
if (errorResult != null && valueType != null) {
if (returnType instanceof PsiArrayType) {
PsiType erasedValueType = TypeConversionUtil.erasure(valueType);
if (erasedValueType != null &&
TypeConversionUtil.isAssignable(((PsiArrayType)returnType).getComponentType(), erasedValueType)) {
QuickFixAction.registerQuickFixAction(errorResult, getFixFactory().createSurroundWithArrayFix(null, returnValue));
}
}
if (!PsiType.VOID.equals(valueType)) {
QuickFixAction.registerQuickFixAction(errorResult, getFixFactory().createMethodReturnFix(method, valueType, true));
}
HighlightFixUtil.registerChangeParameterClassFix(returnType, valueType, errorResult);
HighlightFixUtil.registerCollectionToArrayFixAction(errorResult, valueType, returnType, returnValue);
}
}
}

View File

@@ -0,0 +1,8 @@
// "Convert argument to 'long'" "true-preview"
import java.util.stream.Stream;
public class Demo {
void test() {
Stream<Long> stream = Stream.of(123L).limit(10);
}
}

View File

@@ -0,0 +1,8 @@
// "Apply conversion '.toArray(new java.lang.String[0])'" "true-preview"
import java.util.*;
public class Demo {
void test2(Collection<String> collection) {
Set<String[]> integers = Collections.singleton(collection.toArray(new String[0]));
}
}

View File

@@ -0,0 +1,8 @@
// "Convert argument to 'long'" "true-preview"
import java.util.*;
public class Demo {
void test() {
List<Long> integers = Arrays.asList(1L);
}
}

View File

@@ -0,0 +1,8 @@
// "Surround with array initialization" "true-preview"
import java.util.*;
public class Demo {
void test() {
Set<int[]> integers = Collections.singleton(new int[]{1});
}
}

View File

@@ -0,0 +1,8 @@
// "Convert argument to 'long'" "true-preview"
import java.util.stream.Stream;
public class Demo {
void test() {
Stream<Long> stream = Stream.of(123)<caret>.limit(10);
}
}

View File

@@ -0,0 +1,8 @@
// "Apply conversion '.toArray(new java.lang.String[0])'" "true-preview"
import java.util.*;
public class Demo {
void test2(Collection<String> collection) {
Set<String[]> integers = Collections.<caret>singleton(collection);
}
}

View File

@@ -0,0 +1,8 @@
// "Convert argument to 'long'" "true-preview"
import java.util.*;
public class Demo {
void test() {
List<Long> integers = Arrays.<caret>asList(1);
}
}

View File

@@ -0,0 +1,8 @@
// "Surround with array initialization" "true-preview"
import java.util.*;
public class Demo {
void test() {
Set<int[]> integers = Collections.<caret>singleton(1);
}
}