[java-intentions] IDEA-299075 Better quick-fixes when generic method call has mismatched type

GitOrigin-RevId: 6fa73efcfb43cd7da05f95ef66c2cd40a3dc1a14
This commit is contained in:
Tagir Valeev
2022-08-01 18:12:10 +02:00
committed by intellij-monorepo-bot
parent 46c6cb94ad
commit 8a9959167b
33 changed files with 512 additions and 23 deletions

View File

@@ -616,4 +616,5 @@ inspection.redundant.requires.statement.message.module.unused=No usages of modul
inspection.redundant.requires.statement.message.java.base.implicitly.required='java.base' is implicitly required.
reassign.variable.display.name=Reassigned variable
intention.family.name.remove.repeating.call=Remove repeating call
intention.name.remove.repeating.call=Remove repeating call ''{0}()''
intention.name.remove.repeating.call=Remove repeating call ''{0}()''
intention.family.name.replace.with.expression=Replace with expression

View File

@@ -0,0 +1,199 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceExpressionAction;
import com.intellij.codeInsight.daemon.impl.quickfix.WrapExpressionFix;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.psi.*;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.MethodCallUtils;
import one.util.streamex.MoreCollectors;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
import static com.intellij.util.ObjectUtils.tryCast;
/**
* Utilities to register fixes for mismatching type
*/
class AdaptExpressionTypeFixUtil {
private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance();
private AdaptExpressionTypeFixUtil() {}
static void registerPatchParametersFixes(@NotNull HighlightInfo info,
@NotNull PsiMethodCallExpression call,
@NotNull PsiMethod method,
@NotNull PsiType expectedTypeByParent) {
JavaResolveResult result = call.resolveMethodGenerics();
if (!(result instanceof MethodCandidateInfo)) return;
PsiType methodType = method.getReturnType();
PsiType actualType = ((MethodCandidateInfo)result).getSubstitutor(false).substitute(methodType);
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);
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)) {
if (expectedTypeValue instanceof PsiClassType && JavaGenericsUtil.isReifiableType(expectedTypeValue)) {
ReplaceExpressionAction fix = new ReplaceExpressionAction(
arg, ((PsiClassType)expectedTypeValue).rawType().getCanonicalText() + ".class",
((PsiClassType)expectedTypeValue).rawType().getPresentableText() + ".class");
QuickFixAction.registerQuickFixAction(info, fix);
}
}
PsiType expectedArgType = PsiSubstitutor.EMPTY.put(typeParameter, expectedTypeValue).substitute(parameterType);
if (arg instanceof PsiLambdaExpression) {
registerLambdaReturnFixes(info, (PsiLambdaExpression)arg, parameterType, expectedArgType, typeParameter);
return;
}
registerExpectedTypeFixes(info, arg, expectedArgType);
}
private static void registerLambdaReturnFixes(@NotNull HighlightInfo info,
@NotNull PsiLambdaExpression arg,
@NotNull PsiClassType parameterType,
@Nullable PsiType expectedArgType,
@NotNull PsiTypeParameter typeParameter) {
if (!(expectedArgType instanceof PsiClassType)) return;
PsiExpression lambdaBody = LambdaUtil.extractSingleExpressionFromBody(arg.getBody());
if (lambdaBody == null) return;
PsiClass fnInterface = parameterType.resolve();
if (fnInterface == null) return;
PsiType[] fnArgs = parameterType.getParameters();
PsiTypeParameter[] fnParams = fnInterface.getTypeParameters();
if (fnArgs.length != fnParams.length) return;
PsiType fnTypeArgumentToChange = StreamEx.of(fnArgs)
.collect(MoreCollectors.onlyOne(c -> PsiTypesUtil.mentionsTypeParameters(c, Set.of(typeParameter)))).orElse(null);
if (fnTypeArgumentToChange == null) return;
int index = ArrayUtil.indexOf(fnArgs, fnTypeArgumentToChange);
if (index == -1) return;
PsiTypeParameter fnParam = fnParams[index];
PsiMethod sam = LambdaUtil.getFunctionalInterfaceMethod(fnInterface);
if (sam == null) return;
Set<PsiTypeParameter> fnParamSet = Set.of(fnParam);
PsiType returnType = sam.getReturnType();
if (!PsiTypesUtil.mentionsTypeParameters(returnType, fnParamSet) ||
ContainerUtil.exists(sam.getParameterList().getParameters(), p -> PsiTypesUtil.mentionsTypeParameters(p.getType(), fnParamSet))) {
return;
}
PsiType expectedFnReturnType = LambdaUtil.getFunctionalInterfaceReturnType(expectedArgType);
if (expectedFnReturnType == null) return;
registerExpectedTypeFixes(info, lambdaBody, expectedFnReturnType);
}
static void registerExpectedTypeFixes(@NotNull HighlightInfo info, @NotNull PsiExpression arg, @Nullable PsiType expectedType) {
if (expectedType == null) return;
expectedType = GenericsUtil.getVariableTypeByExpressionType(expectedType);
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapWithAdapterFix(expectedType, arg));
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapWithOptionalFix(expectedType, arg));
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapExpressionFix(expectedType, arg));
PsiType argType = arg.getType();
if (arg instanceof PsiMethodCallExpression) {
JavaResolveResult result = ((PsiMethodCallExpression)arg).resolveMethodGenerics();
if (result instanceof MethodCandidateInfo) {
argType = ((MethodCandidateInfo)result).getSubstitutor(false).substitute(argType);
}
}
PsiType castToType = suggestCastTo(expectedType, argType);
if (castToType != null) {
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddTypeCastFix(castToType, arg));
}
if (arg instanceof PsiMethodCallExpression) {
PsiMethod argMethod = ((PsiMethodCallExpression)arg).resolveMethod();
if (argMethod != null) {
registerPatchParametersFixes(info, (PsiMethodCallExpression)arg, argMethod, expectedType);
}
}
}
private static @Nullable Map.Entry<PsiTypeParameter, PsiType> findDesiredSubstitution(@Nullable PsiType expected,
@Nullable PsiType actual,
@Nullable PsiType methodType) {
if (expected == null || actual == null || methodType == null) return null;
if (expected instanceof PsiArrayType && actual instanceof PsiArrayType && methodType instanceof PsiArrayType) {
return findDesiredSubstitution(((PsiArrayType)expected).getComponentType(),
((PsiArrayType)actual).getComponentType(),
((PsiArrayType)methodType).getComponentType());
}
if (expected instanceof PsiWildcardType && ((PsiWildcardType)expected).isExtends()) {
expected = ((PsiWildcardType)expected).getExtendsBound();
}
if (!(methodType instanceof PsiClassType) || !(expected instanceof PsiClassType) || !(actual 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 instanceof PsiTypeParameter) {
if (!expected.equals(actual)) {
return Map.entry((PsiTypeParameter)methodClass, expected);
}
return null;
}
if (!expectedClass.isEquivalentTo(actualClass) || !expectedClass.isEquivalentTo(methodClass)) return null;
PsiType[] methodTypeParameters = ((PsiClassType)methodType).getParameters();
PsiType[] expectedTypeParameters = ((PsiClassType)expected).getParameters();
PsiType[] actualTypeParameters = ((PsiClassType)actual).getParameters();
if (methodTypeParameters.length != expectedTypeParameters.length || methodTypeParameters.length != actualTypeParameters.length) {
return null;
}
Map.Entry<PsiTypeParameter, PsiType> existing = null;
for (int i = 0; i < methodTypeParameters.length; i++) {
Map.Entry<PsiTypeParameter, PsiType> substitution =
findDesiredSubstitution(expectedTypeParameters[i], actualTypeParameters[i], methodTypeParameters[i]);
if (existing == null) {
existing = substitution;
}
else if (!existing.equals(substitution)) {
return null;
}
}
return existing;
}
private static @Nullable PsiTypeParameter getSoleTypeParameter(@Nullable PsiType type) {
if (!(type instanceof PsiClassType)) return null;
PsiType[] parameters = ((PsiClassType)type).getParameters();
if (parameters.length != 1) return null;
PsiType parameter = parameters[0];
if (parameter instanceof PsiWildcardType) {
parameter = ((PsiWildcardType)parameter).getExtendsBound();
}
if (!(parameter instanceof PsiClassType)) return null;
return tryCast(((PsiClassType)parameter).resolve(), PsiTypeParameter.class);
}
static @Nullable PsiType suggestCastTo(@Nullable PsiType expectedTypeByParent, @Nullable PsiType actualType) {
if (expectedTypeByParent == null || actualType == null) return null;
if (TypeConversionUtil.isAssignable(expectedTypeByParent, actualType)) return null;
if (TypeConversionUtil.areTypesConvertible(actualType, expectedTypeByParent)) return expectedTypeByParent;
if (actualType instanceof PsiPrimitiveType) {
PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(expectedTypeByParent);
if (unboxedType != null && TypeConversionUtil.areTypesConvertible(actualType, unboxedType)) {
return unboxedType;
}
}
return null;
}
}

View File

@@ -325,13 +325,7 @@ public final class HighlightFixUtil {
static void registerLambdaReturnTypeFixes(HighlightInfo info, PsiLambdaExpression lambda, PsiExpression expression) {
PsiType type = LambdaUtil.getFunctionalInterfaceReturnType(lambda);
if (type != null) {
PsiType exprType = expression.getType();
if (exprType != null && TypeConversionUtil.areTypesConvertible(exprType, type)) {
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddTypeCastFix(type, expression));
}
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapWithOptionalFix(type, expression));
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapExpressionFix(type, expression));
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapWithAdapterFix(type, expression));
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(info, expression, type);
}
}

View File

@@ -582,8 +582,7 @@ public final class HighlightMethodUtil {
highlightInfo = HighlightUtil.createIncompatibleTypeHighlightInfo(
expectedTypeByParent, actualType, fixRange, 0, XmlStringUtil.escapeString(errorMessage));
if (methodCall instanceof PsiMethodCallExpression) {
QuickFixAction.registerQuickFixAction(highlightInfo,
QUICK_FIX_FACTORY.createWrapWithAdapterFix(expectedTypeByParent, (PsiExpression)methodCall));
AdaptExpressionTypeFixUtil.registerExpectedTypeFixes(highlightInfo, (PsiMethodCallExpression)methodCall, expectedTypeByParent);
}
}
else {
@@ -946,16 +945,6 @@ public final class HighlightMethodUtil {
QualifySuperArgumentFix.registerQuickFixAction((PsiSuperExpression)qualifierExpression, highlightInfo);
}
PsiType expectedTypeByParent = PsiTypesUtil.getExpectedTypeByParent(methodCall);
if (expectedTypeByParent != null) {
PsiType methodCallType = methodCall.getType();
if (methodCallType != null &&
TypeConversionUtil.areTypesConvertible(methodCallType, expectedTypeByParent) &&
!TypeConversionUtil.isAssignable(expectedTypeByParent, methodCallType)) {
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createAddTypeCastFix(expectedTypeByParent, methodCall));
}
}
CandidateInfo[] methodCandidates = resolveHelper.getReferencedMethodCandidates(methodCall, false);
QuickFixAction.registerQuickFixAction(highlightInfo, fixRange, QUICK_FIX_FACTORY.createSurroundWithArrayFix(methodCall, null));

View File

@@ -568,9 +568,7 @@ public final class HighlightUtil {
QuickFixAction.registerQuickFixAction(highlightInfo, getFixFactory().createAddTypeCastFix(lType, expression));
}
if (expression != null) {
QuickFixAction.registerQuickFixAction(highlightInfo, getFixFactory().createWrapWithOptionalFix(lType, expression));
QuickFixAction.registerQuickFixAction(highlightInfo, getFixFactory().createWrapExpressionFix(lType, expression));
QuickFixAction.registerQuickFixAction(highlightInfo, getFixFactory().createWrapWithAdapterFix(lType, expression));
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);

View File

@@ -0,0 +1,46 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInspection.CommonQuickFixBundle;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiFile;
import com.siyeh.ig.psiutils.CommentTracker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ReplaceExpressionAction extends LocalQuickFixAndIntentionActionOnPsiElement {
private final String myReplacement;
private final String myPresentation;
private final String myOrigText;
public ReplaceExpressionAction(@NotNull PsiExpression expression, @NotNull String replacement, @NotNull String presentation) {
super(expression);
myOrigText = expression.getText();
myReplacement = replacement;
myPresentation = presentation;
}
@Override
public void invoke(@NotNull Project project,
@NotNull PsiFile file,
@Nullable Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
new CommentTracker().replaceAndRestoreComments(startElement, myReplacement);
}
@Override
public @NotNull String getText() {
return CommonQuickFixBundle.message("fix.replace.x.with.y", myOrigText, myPresentation);
}
@Override
public @NotNull String getFamilyName() {
return JavaAnalysisBundle.message("intention.family.name.replace.with.expression");
}
}

View File

@@ -0,0 +1,9 @@
// "Cast to 'java.util.List<java.lang.String>'" "false"
import java.util.*;
class a {
void test() {
List<String> list = Collections.<caret>singletonList(123);
}
}

View File

@@ -0,0 +1,8 @@
// "Replace 'Double.class' with 'Integer.class'" "true-preview"
class Demo {
native static <T> T tryCast(Object obj, Class<T> clazz);
void test(Object obj) {
Integer i = tryCast(obj, Integer.class);
}
}

View File

@@ -0,0 +1,10 @@
// "Replace 'Integer.class' with 'String.class'" "true-preview"
import java.util.*;
class Demo {
native static <T> List<T> filterIsInstance(Collection<?> collection, Class<? extends T> aClass);
void test(List<?> list) {
List<String> strings = filterIsInstance(list, String.class);
}
}

View File

@@ -0,0 +1,10 @@
// "Replace 'Integer.class' with 'String.class'" "true-preview"
import java.util.*;
class Demo {
native static <T> List<T> filterIsInstance(Collection<?> collection, Class<? extends T> aClass);
void test(List<?> list) {
List<String> stringsSync = Collections.synchronizedList(filterIsInstance(list, String.class));
}
}

View File

@@ -0,0 +1,8 @@
// "Cast to 'long'" "true-preview"
import java.util.function.*;
class Demo {
void test(Function<String, String> input) {
Function<String, Long> result = input.andThen(s -> (long) s.length());
}
}

View File

@@ -0,0 +1,8 @@
// "Wrap using 'String.valueOf()'" "true-preview"
import java.util.*;
class Demo {
void test() {
List<String> stringList = Collections.nCopies(10, String.valueOf(20));
}
}

View File

@@ -0,0 +1,8 @@
// "Wrap using 'String.valueOf()'" "true-preview"
import java.util.*;
class Demo {
void test() {
List<Set<String>> nCopiesNested = Collections.nCopies(10, Set.of(String.valueOf(20)));
}
}

View File

@@ -0,0 +1,9 @@
// "Adapt using 'new File()'" "true-preview"
import java.util.*;
import java.io.File;
class Demo {
void test(int value) {
Set<File> file = Set.of(new File("/etc/passwd"));
}
}

View File

@@ -0,0 +1,8 @@
// "Cast to 'long'" "true-preview"
import java.util.*;
class Demo {
void test() {
Optional<Long> opt = Optional.of(123L);
}
}

View File

@@ -0,0 +1,8 @@
// "Wrap using 'String.valueOf()'" "true-preview"
import java.util.*;
class Demo {
void test(int value) {
Optional<String> optStr = Optional.of(String.valueOf(value));
}
}

View File

@@ -0,0 +1,8 @@
// "Wrap using 'String.valueOf()'" "true-preview"
import java.util.stream.*;
class Demo {
void test() {
Stream<String> stream = Stream.generate(() -> String.valueOf(Math.random()));
}
}

View File

@@ -0,0 +1,8 @@
// "Adapt using 'Math.toIntExact()'" "true-preview"
import java.util.*;
class Demo {
void test(List<?> list) {
List<Integer> intList = Collections.singletonList(Math.toIntExact(123L));
}
}

View File

@@ -0,0 +1,8 @@
// "Adapt using 'Math.toIntExact()'" "true-preview"
import java.util.*;
class Demo {
void test(List<?> list) {
List<Integer> intList2 = Collections.unmodifiableList(Collections.singletonList(Math.toIntExact(123L)));
}
}

View File

@@ -0,0 +1,8 @@
// "Replace 'Double.class' with 'Integer.class'" "true-preview"
class Demo {
native static <T> T tryCast(Object obj, Class<T> clazz);
void test(Object obj) {
Integer i = <caret>tryCast(obj, Double.class);
}
}

View File

@@ -0,0 +1,10 @@
// "Replace 'Integer.class' with 'String.class'" "true-preview"
import java.util.*;
class Demo {
native static <T> List<T> filterIsInstance(Collection<?> collection, Class<? extends T> aClass);
void test(List<?> list) {
List<String> strings = <caret>filterIsInstance(list, Integer.class);
}
}

View File

@@ -0,0 +1,10 @@
// "Replace 'Integer.class' with 'String.class'" "true-preview"
import java.util.*;
class Demo {
native static <T> List<T> filterIsInstance(Collection<?> collection, Class<? extends T> aClass);
void test(List<?> list) {
List<String> stringsSync = <caret>Collections.synchronizedList(filterIsInstance(list, Integer.class));
}
}

View File

@@ -0,0 +1,8 @@
// "Cast to 'long'" "true-preview"
import java.util.function.*;
class Demo {
void test(Function<String, String> input) {
Function<String, Long> result = input.andThen(s -> <caret>s.length());
}
}

View File

@@ -0,0 +1,8 @@
// "Wrap using 'String.valueOf()'" "true-preview"
import java.util.*;
class Demo {
void test() {
List<String> stringList = Collections.<caret>nCopies(10, 20);
}
}

View File

@@ -0,0 +1,8 @@
// "Wrap using 'String.valueOf()'" "true-preview"
import java.util.*;
class Demo {
void test() {
List<Set<String>> nCopiesNested = Collections.<caret>nCopies(10, Set.of(20));
}
}

View File

@@ -0,0 +1,9 @@
// "Adapt using 'new File()'" "true-preview"
import java.util.*;
import java.io.File;
class Demo {
void test(int value) {
Set<File> file = Set.of("/etc/<caret>passwd");
}
}

View File

@@ -0,0 +1,8 @@
// "Cast to 'long'" "true-preview"
import java.util.*;
class Demo {
void test() {
Optional<Long> opt = <caret>Optional.of(123);
}
}

View File

@@ -0,0 +1,8 @@
// "Wrap using 'String.valueOf()'" "true-preview"
import java.util.*;
class Demo {
void test(int value) {
Optional<String> optStr = <caret>Optional.of(value);
}
}

View File

@@ -0,0 +1,8 @@
// "Wrap using 'String.valueOf()'" "true-preview"
import java.util.stream.*;
class Demo {
void test() {
Stream<String> stream = Stream.generate(() -> <caret>Math.random());
}
}

View File

@@ -0,0 +1,8 @@
// "Adapt using 'Math.toIntExact()'" "true-preview"
import java.util.*;
class Demo {
void test(List<?> list) {
List<Integer> intList = <caret>Collections.singletonList(123L);
}
}

View File

@@ -0,0 +1,8 @@
// "Adapt using 'Math.toIntExact()'" "true-preview"
import java.util.*;
class Demo {
void test(List<?> list) {
List<Integer> intList2 = <caret>Collections.unmodifiableList(Collections.singletonList(123L));
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.testFramework.LightProjectDescriptor;
import org.jetbrains.annotations.NotNull;
import static com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase.JAVA_11;
public class PatchMethodParametersTest extends LightQuickFixParameterizedTestCase {
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/patchMethodParams";
}
@Override
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
return JAVA_11;
}
}

View File

@@ -499,6 +499,23 @@ public final class MethodCallUtils {
return -1;
}
/**
* @param call method call
* @param parameter parameter of called method
* @return an expression in the call argument list that corresponds to a given parameter; null if there's no correspondence
*/
public static @Nullable PsiExpression getArgumentForParameter(@NotNull PsiCall call, @NotNull PsiParameter parameter) {
PsiMethod scope = tryCast(parameter.getDeclarationScope(), PsiMethod.class);
if (scope == null) return null;
int index = scope.getParameterList().getParameterIndex(parameter);
PsiExpressionList argList = call.getArgumentList();
if (argList == null) return null;
PsiExpression[] args = argList.getExpressions();
if (index >= args.length) return null;
if (parameter.isVarArgs() && !isVarArgCall(call)) return null;
return args[index];
}
private static class SuperCallVisitor extends JavaRecursiveElementWalkingVisitor {
private final PsiMethod myMethod;