From 00d6630674d796da41567da6d3bfd3fe2ed0f6c3 Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Wed, 14 Jun 2017 16:19:01 +0300 Subject: [PATCH] Java: Refactored reflection/MethodHandle references - moved utility methods to more appropriate place (IDEA-172319) --- ...vaLangInvokeHandleSignatureInspection.java | 82 ------------- ...avaLangReflectHandleInvocationChecker.java | 28 ++--- .../JavaReflectionInvocationInspection.java | 59 +-------- .../JavaReflectionMemberAccessInspection.java | 42 +------ .../impl/JavaLangClassMemberReference.java | 65 +++++++++- .../impl/JavaLangInvokeHandleReference.java | 8 +- .../impl/JavaReflectionReferenceUtil.java | 113 ++++++++++++++++++ 7 files changed, 192 insertions(+), 205 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java index e71c07de7568..57273c8c1c74 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java @@ -23,7 +23,6 @@ import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -35,7 +34,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.function.Function; import java.util.stream.Collectors; import static com.intellij.codeInspection.reflectiveAccess.JavaLangReflectVarHandleInvocationChecker.ARRAY_ELEMENT_VAR_HANDLE; @@ -304,86 +302,6 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI .anyMatch(expectedMethodSignature::equals); } - /** - * Take method's return type and parameter types - * from arguments of MethodType.methodType(Class...) and MethodType.genericMethodType(int, boolean?) - */ - @Nullable - public static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) { - final PsiExpression typeDefinition = findDefinition(methodTypeExpression); - if (typeDefinition instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)typeDefinition; - final String referenceName = methodCallExpression.getMethodExpression().getReferenceName(); - - Function composer = null; - if (METHOD_TYPE.equals(referenceName)) { - composer = JavaLangInvokeHandleSignatureInspection::composeMethodSignatureFromTypes; - } - else if (GENERIC_METHOD_TYPE.equals(referenceName)) { - composer = JavaLangInvokeHandleSignatureInspection::composeGenericMethodSignature; - } - - if (composer != null) { - final PsiMethod method = methodCallExpression.resolveMethod(); - if (method != null) { - final PsiClass psiClass = method.getContainingClass(); - if (psiClass != null && JAVA_LANG_INVOKE_METHOD_TYPE.equals(psiClass.getQualifiedName())) { - final PsiExpression[] arguments = methodCallExpression.getArgumentList().getExpressions(); - return composer.apply(arguments); - } - } - } - } - return null; - } - - @Nullable - private static ReflectiveSignature composeMethodSignatureFromTypes(@NotNull PsiExpression[] returnAndParameterTypes) { - final List typeTexts = ContainerUtil.map(returnAndParameterTypes, JavaReflectionReferenceUtil::getTypeText); - return ReflectiveSignature.create(typeTexts); - } - - @Nullable - static Pair.NonNull getGenericSignature(@NotNull PsiExpression[] genericSignatureShape) { - if (genericSignatureShape.length == 0 || genericSignatureShape.length > 2) { - return null; - } - - final Integer objectArgCount = computeConstantExpression(genericSignatureShape[0], Integer.class); - final Boolean finalArray = // there's an additional parameter which is an ellipsis or an array - genericSignatureShape.length > 1 ? computeConstantExpression(genericSignatureShape[1], Boolean.class) : false; - - if (objectArgCount == null || objectArgCount < 0 || objectArgCount > 255) { - return null; - } - if (finalArray == null || finalArray && objectArgCount > 254) { - return null; - } - return Pair.createNonNull(objectArgCount, finalArray); - } - - /** - * All the types in the method signature are either unbounded type parameters or java.lang.Object (with possible vararg) - */ - @Nullable - private static ReflectiveSignature composeGenericMethodSignature(@NotNull PsiExpression[] genericSignatureShape) { - final Pair.NonNull signature = getGenericSignature(genericSignatureShape); - if (signature == null) return null; - final int objectArgCount = signature.getFirst(); - final boolean finalArray = signature.getSecond(); - - final List typeNames = new ArrayList<>(); - typeNames.add(JAVA_LANG_OBJECT); // return type - - for (int i = 0; i < objectArgCount; i++) { - typeNames.add(JAVA_LANG_OBJECT); - } - if (finalArray) { - typeNames.add(JAVA_LANG_OBJECT + "[]"); - } - return ReflectiveSignature.create(typeNames); - } - private static class FieldTypeQuickFix implements LocalQuickFix { private final String myFieldTypeText; diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectHandleInvocationChecker.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectHandleInvocationChecker.java index d0011eec9816..8d4714f0e00c 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectHandleInvocationChecker.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangReflectHandleInvocationChecker.java @@ -271,27 +271,25 @@ class JavaLangReflectHandleInvocationChecker { } else if (isCallToMethod(typeDefinitionCall, JAVA_LANG_INVOKE_METHOD_TYPE, GENERIC_METHOD_TYPE)) { final PsiExpression[] arguments = typeDefinitionCall.getArgumentList().getExpressions(); - final Pair.NonNull signature = JavaLangInvokeHandleSignatureInspection.getGenericSignature(arguments); + final Pair.NonNull signature = getGenericSignature(arguments); if (signature != null) { final int objectArgCount = signature.getFirst(); final boolean finalArray = signature.getSecond(); if (objectArgCount == 0 && !finalArray) { return Collections.emptyList(); } - final JavaPsiFacade facade = JavaPsiFacade.getInstance(methodTypeExpression.getProject()); - final PsiClass objectClass = facade.findClass(CommonClassNames.JAVA_LANG_OBJECT, methodTypeExpression.getResolveScope()); - if (objectClass != null) { - final List argumentTypes = new ArrayList<>(); - final ReflectiveType objectType = ReflectiveType.create(objectClass); - argumentTypes.add(objectType); // return type - for (int i = 0; i < objectArgCount; i++) { - argumentTypes.add(objectType); - } - if (finalArray) { - argumentTypes.add(ReflectiveType.arrayOf(objectType)); - } - return ContainerUtil.map(argumentTypes, type -> (() -> type)); + final PsiClassType javaLangObject = + PsiType.getJavaLangObject(methodTypeExpression.getManager(), methodTypeExpression.getResolveScope()); + final ReflectiveType objectType = ReflectiveType.create(javaLangObject); + final List argumentTypes = new ArrayList<>(); + argumentTypes.add(objectType); // return type + for (int i = 0; i < objectArgCount; i++) { + argumentTypes.add(objectType); } + if (finalArray) { + argumentTypes.add(ReflectiveType.arrayOf(objectType)); + } + return ContainerUtil.map(argumentTypes, type -> (() -> type)); } } } @@ -364,7 +362,7 @@ class JavaLangReflectHandleInvocationChecker { if (INVOKE_WITH_ARGUMENTS.equals(invokeCall.getMethodExpression().getReferenceName())) { final PsiExpression[] arguments = invokeCall.getArgumentList().getExpressions(); if (arguments.length == 1) { - return JavaReflectionInvocationInspection.isVarargAsArray(arguments[0]) || + return isVarargAsArray(arguments[0]) || InheritanceUtil.isInheritor(arguments[0].getType(), JAVA_UTIL_LIST); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java index 6f3515fceafe..1c6da905ad8e 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionInvocationInspection.java @@ -19,10 +19,9 @@ import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.reference.impl.JavaLangClassMemberReference; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.ParenthesesUtils; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -132,34 +131,12 @@ public class JavaReflectionInvocationInspection extends BaseJavaBatchLocalInspec if (definition instanceof PsiMethodCallExpression) { final PsiMethodCallExpression definitionCall = (PsiMethodCallExpression)definition; if (methodPredicate.test(definitionCall)) { - return getRequiredMethodArguments(definitionCall, argumentOffset); + return JavaLangClassMemberReference.getReflectionMethodArguments(definitionCall, argumentOffset); } } return null; } - private static List getRequiredMethodArguments(@NotNull PsiMethodCallExpression definitionCall, int argumentOffset) { - final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions(); - - if (arguments.length == argumentOffset + 1) { - final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]); - if (arrayElements != null) { - return Arrays.asList(arrayElements); - } - } - if (arguments.length >= argumentOffset) { - return Arrays.asList(arguments).subList(argumentOffset, arguments.length); - } - return null; - } - - @Nullable - public static List getReflectionMethodParameterTypes(@NotNull PsiMethodCallExpression definitionCall, - int argumentOffset) { - List arguments = getRequiredMethodArguments(definitionCall, argumentOffset); - return arguments != null ? ContainerUtil.map(arguments, type -> getReflectiveType(type)) : null; - } - @Nullable static Arguments getActualMethodArguments(PsiExpression[] arguments, int argumentOffset, boolean allowVarargAsArray) { if (allowVarargAsArray && arguments.length == argumentOffset + 1) { @@ -181,38 +158,6 @@ public class JavaReflectionInvocationInspection extends BaseJavaBatchLocalInspec return null; } - @Nullable - private static PsiExpression[] getVarargAsArray(@Nullable PsiExpression maybeArray) { - if (isVarargAsArray(maybeArray)) { - final PsiExpression argumentsDefinition = findDefinition(maybeArray); - if (argumentsDefinition instanceof PsiArrayInitializerExpression) { - return ((PsiArrayInitializerExpression)argumentsDefinition).getInitializers(); - } - if (argumentsDefinition instanceof PsiNewExpression) { - final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)argumentsDefinition).getArrayInitializer(); - if (arrayInitializer != null) { - return arrayInitializer.getInitializers(); - } - final PsiExpression[] dimensions = ((PsiNewExpression)argumentsDefinition).getArrayDimensions(); - if (dimensions.length == 1) { // special case: new Object[0] - final Integer itemCount = computeConstantExpression(findDefinition(dimensions[0]), Integer.class); - if (itemCount != null && itemCount == 0) { - return PsiExpression.EMPTY_ARRAY; - } - } - } - } - return null; - } - - @Contract("null -> false") - static boolean isVarargAsArray(@Nullable PsiExpression maybeArray) { - final PsiType type = maybeArray != null ? maybeArray.getType() : null; - return type instanceof PsiArrayType && - type.getArrayDimensions() == 1 && - type.getDeepComponentType() instanceof PsiClassType; - } - @Nullable private static PsiExpression unwrapDisambiguatingCastToObject(@Nullable PsiExpression expression) { if (expression instanceof PsiTypeCastExpression) { diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java index 90213c251cfa..019e593c442a 100644 --- a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaReflectionMemberAccessInspection.java @@ -23,6 +23,7 @@ import com.intellij.codeInspection.ui.ListWrappingTableModel; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.reference.impl.JavaLangClassMemberReference; import com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.CheckBox; @@ -279,45 +280,6 @@ public class JavaReflectionMemberAccessInspection extends BaseJavaBatchLocalInsp final List argumentTypes = ContainerUtil.map(methodArguments.expressions, JavaReflectionReferenceUtil::getReflectiveType); - return matchMethod(methods, argumentTypes); - } - - @Nullable - public static PsiMethod matchMethod(@NotNull PsiMethod[] methods, @NotNull List argumentTypes) { - int mismatchCount = Integer.MAX_VALUE; - PsiMethod bestGuess = null; - for (PsiMethod method : methods) { - final int match = matchMethodArguments(method, argumentTypes); - if (match == 0) { - return method; - } - if (match < 0) { - continue; - } - if (mismatchCount > match) { - mismatchCount = match; - bestGuess = method; - } - } - return bestGuess; - } - - private static int matchMethodArguments(PsiMethod method, List argumentTypes) { - final PsiParameter[] parameters = method.getParameterList().getParameters(); - if (parameters.length != argumentTypes.size()) { - return -1; - } - int mismatchCount = 0; - for (int i = 0; i < parameters.length; i++) { - final ReflectiveType argumentType = argumentTypes.get(i); - if (argumentType == null) { - mismatchCount++; - continue; - } - if (!argumentType.isEqualTo(parameters[i].getType())) { - return -1; - } - } - return mismatchCount; + return JavaLangClassMemberReference.matchMethod(methods, argumentTypes); } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java index 00ba95c77149..4e45bdf5b3c8 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java @@ -18,8 +18,6 @@ package com.intellij.psi.impl.source.resolve.reference.impl; import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInspection.reflectiveAccess.JavaReflectionInvocationInspection; -import com.intellij.codeInspection.reflectiveAccess.JavaReflectionMemberAccessInspection; import com.intellij.psi.*; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; import com.intellij.psi.util.PsiTreeUtil; @@ -170,9 +168,10 @@ public class JavaLangClassMemberReference extends PsiReferenceBase parameterTypes = JavaReflectionInvocationInspection.getReflectionMethodParameterTypes(definitionCall, 1); - if (parameterTypes != null) { - return JavaReflectionMemberAccessInspection.matchMethod(methods, parameterTypes); + final List arguments = getReflectionMethodArguments(definitionCall, 1); + if (arguments != null) { + final List parameterTypes = ContainerUtil.map(arguments, type -> getReflectiveType(type)); + return matchMethod(methods, parameterTypes); } } return null; @@ -187,4 +186,60 @@ public class JavaLangClassMemberReference extends PsiReferenceBase argumentTypes) { + int mismatchCount = Integer.MAX_VALUE; + PsiMethod bestGuess = null; + for (PsiMethod method : methods) { + final int match = matchMethodArguments(method, argumentTypes); + if (match == 0) { + return method; + } + if (match < 0) { + continue; + } + if (mismatchCount > match) { + mismatchCount = match; + bestGuess = method; + } + } + return bestGuess; + } + + private static int matchMethodArguments(PsiMethod method, List argumentTypes) { + final PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameters.length != argumentTypes.size()) { + return -1; + } + int mismatchCount = 0; + for (int i = 0; i < parameters.length; i++) { + final ReflectiveType argumentType = argumentTypes.get(i); + if (argumentType == null) { + mismatchCount++; + continue; + } + if (!argumentType.isEqualTo(parameters[i].getType())) { + return -1; + } + } + return mismatchCount; + } + + @Nullable + public static List getReflectionMethodArguments(@NotNull PsiMethodCallExpression definitionCall, int argumentOffset) { + final PsiExpression[] arguments = definitionCall.getArgumentList().getExpressions(); + + if (arguments.length == argumentOffset + 1) { + final PsiExpression[] arrayElements = getVarargAsArray(arguments[argumentOffset]); + if (arrayElements != null) { + return Arrays.asList(arrayElements); + } + } + if (arguments.length >= argumentOffset) { + return Arrays.asList(arguments).subList(argumentOffset, arguments.length); + } + return null; + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java index 2574d768a80e..ed949cfc0e5b 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangInvokeHandleReference.java @@ -19,7 +19,6 @@ import com.intellij.codeInsight.completion.InsertHandler; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.completion.JavaLookupElementBuilder; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; @@ -33,10 +32,7 @@ import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.function.Predicate; import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*; @@ -110,7 +106,7 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase 2) { final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[2]); - final ReflectiveSignature expectedSignature = JavaLangInvokeHandleSignatureInspection.composeMethodSignature(typeExpression); + final ReflectiveSignature expectedSignature = composeMethodSignature(typeExpression); if (expectedSignature != null) { return ContainerUtil.find(methods, method -> expectedSignature.equals(getMethodSignature(method))); } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java index c6aaaeda6eee..7969d19b400c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java @@ -23,6 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.RecursionGuard; import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.*; @@ -441,6 +442,118 @@ public class JavaReflectionReferenceUtil { return MethodCallUtils.isCallToMethod(methodCall, className, null, methodName, (PsiType[])null); } + @Nullable + public static PsiExpression[] getVarargAsArray(@Nullable PsiExpression maybeArray) { + if (isVarargAsArray(maybeArray)) { + final PsiExpression argumentsDefinition = findDefinition(maybeArray); + if (argumentsDefinition instanceof PsiArrayInitializerExpression) { + return ((PsiArrayInitializerExpression)argumentsDefinition).getInitializers(); + } + if (argumentsDefinition instanceof PsiNewExpression) { + final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)argumentsDefinition).getArrayInitializer(); + if (arrayInitializer != null) { + return arrayInitializer.getInitializers(); + } + final PsiExpression[] dimensions = ((PsiNewExpression)argumentsDefinition).getArrayDimensions(); + if (dimensions.length == 1) { // special case: new Object[0] + final Integer itemCount = computeConstantExpression(findDefinition(dimensions[0]), Integer.class); + if (itemCount != null && itemCount == 0) { + return PsiExpression.EMPTY_ARRAY; + } + } + } + } + return null; + } + + @Contract("null -> false") + public static boolean isVarargAsArray(@Nullable PsiExpression maybeArray) { + final PsiType type = maybeArray != null ? maybeArray.getType() : null; + return type instanceof PsiArrayType && + type.getArrayDimensions() == 1 && + type.getDeepComponentType() instanceof PsiClassType; + } + + /** + * Take method's return type and parameter types + * from arguments of MethodType.methodType(Class...) and MethodType.genericMethodType(int, boolean?) + */ + @Nullable + public static ReflectiveSignature composeMethodSignature(@Nullable PsiExpression methodTypeExpression) { + final PsiExpression typeDefinition = findDefinition(methodTypeExpression); + if (typeDefinition instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)typeDefinition; + final String referenceName = methodCallExpression.getMethodExpression().getReferenceName(); + + Function composer = null; + if (METHOD_TYPE.equals(referenceName)) { + composer = JavaReflectionReferenceUtil::composeMethodSignatureFromTypes; + } + else if (GENERIC_METHOD_TYPE.equals(referenceName)) { + composer = JavaReflectionReferenceUtil::composeGenericMethodSignature; + } + + if (composer != null) { + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method != null) { + final PsiClass psiClass = method.getContainingClass(); + if (psiClass != null && JAVA_LANG_INVOKE_METHOD_TYPE.equals(psiClass.getQualifiedName())) { + final PsiExpression[] arguments = methodCallExpression.getArgumentList().getExpressions(); + return composer.apply(arguments); + } + } + } + } + return null; + } + + @Nullable + private static ReflectiveSignature composeMethodSignatureFromTypes(@NotNull PsiExpression[] returnAndParameterTypes) { + final List typeTexts = ContainerUtil.map(returnAndParameterTypes, JavaReflectionReferenceUtil::getTypeText); + return ReflectiveSignature.create(typeTexts); + } + + @Nullable + public static Pair.NonNull getGenericSignature(@NotNull PsiExpression[] genericSignatureShape) { + if (genericSignatureShape.length == 0 || genericSignatureShape.length > 2) { + return null; + } + + final Integer objectArgCount = computeConstantExpression(genericSignatureShape[0], Integer.class); + final Boolean finalArray = // there's an additional parameter which is an ellipsis or an array + genericSignatureShape.length > 1 ? computeConstantExpression(genericSignatureShape[1], Boolean.class) : false; + + if (objectArgCount == null || objectArgCount < 0 || objectArgCount > 255) { + return null; + } + if (finalArray == null || finalArray && objectArgCount > 254) { + return null; + } + return Pair.createNonNull(objectArgCount, finalArray); + } + + /** + * All the types in the method signature are either unbounded type parameters or java.lang.Object (with possible vararg) + */ + @Nullable + private static ReflectiveSignature composeGenericMethodSignature(@NotNull PsiExpression[] genericSignatureShape) { + final Pair.NonNull signature = getGenericSignature(genericSignatureShape); + if (signature == null) return null; + final int objectArgCount = signature.getFirst(); + final boolean finalArray = signature.getSecond(); + + final List typeNames = new ArrayList<>(); + typeNames.add(CommonClassNames.JAVA_LANG_OBJECT); // return type + + for (int i = 0; i < objectArgCount; i++) { + typeNames.add(CommonClassNames.JAVA_LANG_OBJECT); + } + if (finalArray) { + typeNames.add(CommonClassNames.JAVA_LANG_OBJECT + "[]"); + } + return ReflectiveSignature.create(typeNames); + } + public static class ReflectiveType { final PsiType myType;