[java-psi] Rework extension methods support, according to review IJ-CR-4151

GitOrigin-RevId: 29d547dc38b2feba7e55b8fcfb420094341219b8
This commit is contained in:
Tagir Valeev
2020-12-03 12:30:22 +07:00
committed by intellij-monorepo-bot
parent 52a6df4733
commit 33edc0ffa4
7 changed files with 85 additions and 84 deletions

View File

@@ -5,7 +5,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.augment.PsiExtensionMethod;
import com.intellij.psi.impl.light.LightRecordCanonicalConstructor;
import com.intellij.psi.impl.light.LightRecordMember;
import com.intellij.psi.javadoc.PsiDocTag;
@@ -152,11 +152,8 @@ public class JavaTargetElementEvaluator extends TargetElementEvaluatorEx2 implem
}
}
if (refElement instanceof PsiMethod) {
PsiMethod extensionMethod = PsiAugmentProvider.resolveExtensionMethod((PsiMethod)refElement);
if (extensionMethod != null) {
return extensionMethod;
}
if (refElement instanceof PsiExtensionMethod) {
refElement = ((PsiExtensionMethod)refElement).getTargetMethod();
}
if (refElement instanceof LightRecordMember) {

View File

@@ -74,19 +74,10 @@ public abstract class PsiAugmentProvider {
* @param context context where extension methods should be applicable
*/
@ApiStatus.Experimental
protected List<PsiMethod> getExtensionMethods(@NotNull PsiClass aClass, @NotNull String nameHint, @NotNull PsiElement context) {
protected List<PsiExtensionMethod> getExtensionMethods(@NotNull PsiClass aClass, @NotNull String nameHint, @NotNull PsiElement context) {
return Collections.emptyList();
}
/**
* @param method a method to resolve
* @return target static method, or null if the supplied method is not an extension method
*/
@ApiStatus.Experimental
protected @Nullable PsiMethod getTargetMethod(@NotNull PsiMethod method) {
return null;
}
/**
* @deprecated invoke and override {@link #getAugments(PsiElement, Class, String)}.
*/
@@ -164,11 +155,11 @@ public abstract class PsiAugmentProvider {
@ApiStatus.Experimental
@NotNull
public static List<PsiMethod> collectExtensionMethods(PsiClass aClass, @NotNull String nameHint, PsiElement context) {
List<PsiMethod> extensionMethods = new SmartList<>();
public static List<PsiExtensionMethod> collectExtensionMethods(PsiClass aClass, @NotNull String nameHint, PsiElement context) {
List<PsiExtensionMethod> extensionMethods = new SmartList<>();
forEach(aClass.getProject(), provider -> {
List<PsiMethod> methods = provider.getExtensionMethods(aClass, nameHint, context);
for (PsiMethod method : methods) {
List<PsiExtensionMethod> methods = provider.getExtensionMethods(aClass, nameHint, context);
for (PsiExtensionMethod method : methods) {
try {
PsiUtilCore.ensureValid(method);
extensionMethods.add(method);
@@ -185,25 +176,6 @@ public abstract class PsiAugmentProvider {
return extensionMethods;
}
/**
* @param method a method to resolve
* @return target static method, or null if the supplied method is not an extension method
*/
@ApiStatus.Experimental
@Nullable
public static PsiMethod resolveExtensionMethod(PsiMethod method) {
Ref<PsiMethod> result = Ref.create();
forEach(method.getProject(), provider -> {
PsiMethod target = provider.getTargetMethod(method);
if (target != null) {
result.set(target);
return false;
}
return true;
});
return result.get();
}
@Nullable
public static PsiType getInferredType(@NotNull PsiTypeElement typeElement) {
Ref<PsiType> result = Ref.create();

View File

@@ -0,0 +1,33 @@
// Copyright 2000-2020 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.
package com.intellij.psi.augment;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* An extension instance method that delegates to another method.
* Does not exist normally in Java but may be supported by annotation processors or other language extensions.
*/
@ApiStatus.Experimental
public interface PsiExtensionMethod extends PsiMethod {
/**
* @return a target method
*/
@NotNull PsiMethod getTargetMethod();
/**
* @return a target method parameter that corresponds to the receiver of this extension method;
* null if the receiver does not correspond to any parameter of the target method.
*/
@Nullable PsiParameter getTargetReceiverParameter();
/**
* @param index index of this method parameter
* @return a target method parameter that corresponds to the parameter of this extension method having the specified index;
* null if the specified parameter does not correspond to any parameter of the target method.
*/
@Nullable PsiParameter getTargetParameter(int index);
}

View File

@@ -17,6 +17,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.augment.PsiExtensionMethod;
import com.intellij.psi.impl.java.stubs.PsiClassReferenceListStub;
import com.intellij.psi.impl.source.ClassInnerStuffCache;
import com.intellij.psi.impl.source.PsiImmediateClassType;
@@ -359,7 +360,7 @@ public final class PsiClassImplUtil {
}
}
if (type == MemberType.METHOD && psiClass != null && context != null) {
List<PsiMethod> methods = PsiAugmentProvider.collectExtensionMethods(psiClass, name, context);
List<PsiExtensionMethod> methods = PsiAugmentProvider.collectExtensionMethods(psiClass, name, context);
if (!methods.isEmpty()) {
if (result == null) result = new ArrayList<>();
result.addAll(methods);

View File

@@ -18,7 +18,7 @@ package com.siyeh.ig.psiutils;
import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.augment.PsiExtensionMethod;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
@@ -438,11 +438,8 @@ public final class MethodCallUtils {
PsiMethodCallExpression callForQualifier = tryCast(argumentParent.getParent(), PsiMethodCallExpression.class);
if (callForQualifier != null) {
PsiMethod method = callForQualifier.resolveMethod();
if (method != null) {
PsiMethod extensionMethod = PsiAugmentProvider.resolveExtensionMethod(method);
if (extensionMethod != null) {
return extensionMethod.getParameterList().getParameter(0);
}
if (method instanceof PsiExtensionMethod) {
return ((PsiExtensionMethod)method).getTargetReceiverParameter();
}
}
}
@@ -459,15 +456,11 @@ public final class MethodCallUtils {
if (index == -1) return null;
PsiMethod method = call.resolveMethod();
if (method == null) return null;
PsiMethod extensionMethod = PsiAugmentProvider.resolveExtensionMethod(method);
if (extensionMethod != null) {
method = extensionMethod;
index++;
}
PsiParameter[] parameters = method.getParameterList().getParameters();
if (index >= parameters.length) return null;
if (isVarArgCall(call) && index >= parameters.length - 1) return null;
return parameters[index];
PsiParameterList list = method.getParameterList();
int count = list.getParametersCount();
if (index >= count) return null;
if (isVarArgCall(call) && index >= count - 1) return null;
return method instanceof PsiExtensionMethod ? ((PsiExtensionMethod)method).getTargetParameter(index) : list.getParameter(index);
}
private static int getParameterReferenceIndex(PsiMethodCallExpression call, PsiParameter parameter) {

View File

@@ -5,6 +5,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiExtensionMethod;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import com.intellij.util.SmartList;
@@ -25,9 +26,9 @@ public class ExtensionMethodsHelper {
private static final Logger LOG = Logger.getInstance(ExtensionMethodsHelper.class);
public static List<PsiMethod> getExtensionMethods(final @NotNull PsiClass targetClass,
final @NotNull String nameHint,
final @NotNull PsiElement place) {
public static List<PsiExtensionMethod> getExtensionMethods(final @NotNull PsiClass targetClass,
final @NotNull String nameHint,
final @NotNull PsiElement place) {
if (!(place instanceof PsiMethodCallExpression)) {
return Collections.emptyList();
}
@@ -38,7 +39,7 @@ public class ExtensionMethodsHelper {
qualifierExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifierExpression).resolve() instanceof PsiClass) {
return Collections.emptyList();
}
List<PsiMethod> result = new SmartList<>();
List<PsiExtensionMethod> result = new SmartList<>();
@Nullable PsiClass context = PsiTreeUtil.getContextOfType(place, PsiClass.class);
while (context != null) {
@@ -53,13 +54,13 @@ public class ExtensionMethodsHelper {
.collect(Collectors.toSet());
if (!providers.isEmpty()) {
List<PsiMethod> extensionMethods = collectExtensionMethods(providers, ((PsiMethodCallExpression)place), targetClass);
List<PsiExtensionMethod> extensionMethods = collectExtensionMethods(providers, ((PsiMethodCallExpression)place), targetClass);
extensionMethods
.stream()
.map(method -> MethodSignatureBackedByPsiMethod.create(method, PsiSubstitutor.EMPTY))
.distinct()
.filter(methodSignature -> !targetClass.getVisibleSignatures().contains(methodSignature))
.forEach(methodSignature -> result.add(methodSignature.getMethod()));
.forEach(methodSignature -> result.add((PsiExtensionMethod)methodSignature.getMethod()));
}
}
context = PsiTreeUtil.getContextOfType(context, PsiClass.class);
@@ -67,21 +68,21 @@ public class ExtensionMethodsHelper {
return result;
}
private static List<PsiMethod> collectExtensionMethods(final Set<PsiClass> providers,
private static List<PsiExtensionMethod> collectExtensionMethods(final Set<PsiClass> providers,
final PsiMethodCallExpression callExpression,
final PsiClass targetClass) {
List<PsiMethod> psiMethods = new ArrayList<>();
List<PsiExtensionMethod> psiMethods = new ArrayList<>();
providers.forEach(providerClass -> providerData(providerClass).forEach(function -> ContainerUtil.addIfNotNull(psiMethods, function.apply(targetClass, callExpression))));
return psiMethods;
}
public static List<BiFunction<PsiClass, PsiMethodCallExpression, PsiMethod>> providerData(final PsiClass providerClass) {
public static List<BiFunction<PsiClass, PsiMethodCallExpression, PsiExtensionMethod>> providerData(final PsiClass providerClass) {
return CachedValuesManager.getCachedValue(providerClass, () -> CachedValueProvider.Result
.create(createProviderCandidates(providerClass), PsiModificationTracker.MODIFICATION_COUNT));
}
private static List<BiFunction<PsiClass, PsiMethodCallExpression, PsiMethod>> createProviderCandidates(final PsiClass providerClass) {
final List<BiFunction<PsiClass, PsiMethodCallExpression, PsiMethod>> result = new ArrayList<>();
private static List<BiFunction<PsiClass, PsiMethodCallExpression, PsiExtensionMethod>> createProviderCandidates(final PsiClass providerClass) {
final List<BiFunction<PsiClass, PsiMethodCallExpression, PsiExtensionMethod>> result = new ArrayList<>();
for (PsiMethod providerStaticMethod : PsiClassUtil.collectClassStaticMethodsIntern(providerClass)) {
if (providerStaticMethod.hasModifierProperty(PsiModifier.PUBLIC)) {
PsiParameter @NotNull [] parameters = providerStaticMethod.getParameterList().getParameters();
@@ -93,7 +94,7 @@ public class ExtensionMethodsHelper {
return result;
}
private static LombokLightMethodBuilder createLightMethodBySignature(PsiMethod staticMethod,
private static PsiExtensionMethod createLightMethodBySignature(PsiMethod staticMethod,
PsiClass targetClass,
PsiMethodCallExpression callExpression) {
if (!staticMethod.getName().equals(callExpression.getMethodExpression().getReferenceName())) return null;
@@ -124,7 +125,7 @@ public class ExtensionMethodsHelper {
if (!method.equals(staticMethod) || !((MethodCandidateInfo)result).isApplicable()) return null;
PsiSubstitutor substitutor = result.getSubstitutor();
final LombokLightMethodBuilder lightMethod = new LombokExtensionMethod(staticMethod);
final LombokExtensionMethod lightMethod = new LombokExtensionMethod(staticMethod);
lightMethod
.addModifiers(PsiModifier.PUBLIC);
PsiParameter @NotNull [] parameters = staticMethod.getParameterList().getParameters();
@@ -156,14 +157,7 @@ public class ExtensionMethodsHelper {
return lightMethod;
}
public static @Nullable PsiMethod resolve(@NotNull PsiMethod method) {
if (method instanceof LombokExtensionMethod) {
return ((LombokExtensionMethod)method).myStaticMethod;
}
return null;
}
private static class LombokExtensionMethod extends LombokLightMethodBuilder {
private static class LombokExtensionMethod extends LombokLightMethodBuilder implements PsiExtensionMethod {
private final @NotNull PsiMethod myStaticMethod;
LombokExtensionMethod(@NotNull PsiMethod staticMethod) {
@@ -173,5 +167,20 @@ public class ExtensionMethodsHelper {
@Override
public boolean isEquivalentTo(final PsiElement another) { return myStaticMethod.isEquivalentTo(another); }
@Override
public @NotNull PsiMethod getTargetMethod() {
return myStaticMethod;
}
@Override
public @Nullable PsiParameter getTargetReceiverParameter() {
return myStaticMethod.getParameterList().getParameter(0);
}
@Override
public @Nullable PsiParameter getTargetParameter(int index) {
return myStaticMethod.getParameterList().getParameter(index + 1);
}
}
}

View File

@@ -6,6 +6,7 @@ import com.intellij.openapi.util.RecursionGuard;
import com.intellij.openapi.util.RecursionManager;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.augment.PsiExtensionMethod;
import com.intellij.psi.impl.source.PsiExtensibleClass;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
@@ -162,17 +163,12 @@ public class LombokAugmentProvider extends PsiAugmentProvider {
}
@Override
protected List<PsiMethod> getExtensionMethods(@NotNull PsiClass aClass,
@NotNull String nameHint,
@NotNull PsiElement context) {
protected List<PsiExtensionMethod> getExtensionMethods(@NotNull PsiClass aClass,
@NotNull String nameHint,
@NotNull PsiElement context) {
if (!LombokProjectValidatorActivity.hasLombokLibrary(context.getProject())) {
return Collections.emptyList();
}
return ExtensionMethodsHelper.getExtensionMethods(aClass, nameHint, context);
}
@Override
protected @Nullable PsiMethod getTargetMethod(@NotNull PsiMethod method) {
return ExtensionMethodsHelper.resolve(method);
}
}