mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 21:55:01 +07:00
smart completion of method references (IDEA-123293; IDEA-123291)
This commit is contained in:
+1
-1
@@ -83,7 +83,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
|
||||
new GeneratorFilter(AssignableGroupFilter.class, new ThrowsListGetter()),
|
||||
new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE));
|
||||
public static final ElementPattern<PsiElement> INSIDE_EXPRESSION = or(
|
||||
psiElement().withParent(PsiExpression.class).andNot(psiElement().withParent(PsiLiteralExpression.class)),
|
||||
psiElement().withParent(PsiExpression.class).andNot(psiElement().withParent(PsiLiteralExpression.class)).andNot(psiElement().withParent(PsiMethodReferenceExpression.class)),
|
||||
psiElement().inside(PsiClassObjectAccessExpression.class),
|
||||
psiElement().inside(PsiThisExpression.class),
|
||||
psiElement().inside(PsiSuperExpression.class)
|
||||
|
||||
+27
-3
@@ -19,14 +19,18 @@ import com.intellij.codeInsight.ExpectedTypeInfo;
|
||||
import com.intellij.codeInsight.ExpectedTypeInfoImpl;
|
||||
import com.intellij.codeInsight.TailType;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.psi.LambdaUtil;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class MethodReferenceCompletionProvider extends CompletionProvider<CompletionParameters> {
|
||||
private static final Logger LOG = Logger.getInstance("#" + MethodReferenceCompletionProvider.class.getName());
|
||||
|
||||
@Override
|
||||
protected void addCompletions(@NotNull CompletionParameters parameters,
|
||||
ProcessingContext context,
|
||||
@@ -38,13 +42,33 @@ public class MethodReferenceCompletionProvider extends CompletionProvider<Comple
|
||||
if (LambdaUtil.isFunctionalType(defaultType)) {
|
||||
final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(defaultType);
|
||||
if (returnType != null) {
|
||||
final PsiMethodReferenceExpression ref = (PsiMethodReferenceExpression)parameters.getPosition().getParent();
|
||||
final ExpectedTypeInfoImpl typeInfo =
|
||||
new ExpectedTypeInfoImpl(returnType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, returnType, TailType.UNKNOWN, null,
|
||||
ExpectedTypeInfoImpl.NULL);
|
||||
final Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.getFunctionalTypeMap();
|
||||
Consumer<LookupElement> noTypeCheck = new Consumer<LookupElement>() {
|
||||
@Override
|
||||
public void consume(final LookupElement lookupElement) {
|
||||
result.addElement(lookupElement);
|
||||
final PsiElement element = lookupElement.getPsiElement();
|
||||
if (element instanceof PsiMethod) {
|
||||
final PsiMethodReferenceExpression referenceExpression = (PsiMethodReferenceExpression)ref.copy();
|
||||
final PsiElement referenceNameElement = referenceExpression.getReferenceNameElement();
|
||||
LOG.assertTrue(referenceNameElement != null, referenceExpression);
|
||||
referenceNameElement.replace(JavaPsiFacade.getElementFactory(element.getProject()).createIdentifier(((PsiMethod)element).getName()));
|
||||
final PsiType added = map.put(referenceExpression, defaultType);
|
||||
try {
|
||||
final PsiElement resolve = referenceExpression.resolve();
|
||||
if (resolve == element && PsiMethodReferenceUtil.checkMethodReferenceContext(referenceExpression, resolve, defaultType) == null) {
|
||||
result.addElement(lookupElement);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (added == null) {
|
||||
map.remove(referenceExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.intellij.psi;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -86,6 +87,16 @@ public class PsiMethodReferenceUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<PsiMethodReferenceExpression, PsiType> getFunctionalTypeMap() {
|
||||
Map<PsiMethodReferenceExpression, PsiType> map = ourRefs.get();
|
||||
if (map == null) {
|
||||
map = new HashMap<PsiMethodReferenceExpression, PsiType>();
|
||||
ourRefs.set(map);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static class QualifierResolveResult {
|
||||
private final PsiClass myContainingClass;
|
||||
private final PsiSubstitutor mySubstitutor;
|
||||
@@ -232,6 +243,12 @@ public class PsiMethodReferenceUtil {
|
||||
final PsiElement resolve = methodRef.resolve();
|
||||
|
||||
if (resolve == null) return null;
|
||||
return checkMethodReferenceContext(methodRef, resolve, methodRef.getFunctionalInterfaceType());
|
||||
}
|
||||
|
||||
public static String checkMethodReferenceContext(PsiMethodReferenceExpression methodRef,
|
||||
PsiElement resolve,
|
||||
PsiType functionalInterfaceType) {
|
||||
final PsiClass containingClass = resolve instanceof PsiMethod ? ((PsiMethod)resolve).getContainingClass() : (PsiClass)resolve;
|
||||
final boolean isStaticSelector = isStaticallyReferenced(methodRef);
|
||||
final PsiElement qualifier = methodRef.getQualifier();
|
||||
@@ -245,7 +262,7 @@ public class PsiMethodReferenceUtil {
|
||||
|
||||
isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC);
|
||||
isConstructor = method.isConstructor();
|
||||
receiverReferenced = hasReceiver(methodRef, method);
|
||||
receiverReferenced = hasReceiver(methodRef, method, functionalInterfaceType);
|
||||
|
||||
if (method.hasModifierProperty(PsiModifier.ABSTRACT) && qualifier instanceof PsiSuperExpression) {
|
||||
return "Abstract method '" + method.getName() + "' cannot be accessed directly";
|
||||
@@ -285,8 +302,15 @@ public class PsiMethodReferenceUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean hasReceiver(@NotNull PsiMethodReferenceExpression methodRef, @NotNull PsiMethod method) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(methodRef.getFunctionalInterfaceType());
|
||||
public static boolean hasReceiver(@NotNull PsiMethodReferenceExpression methodRef,
|
||||
@NotNull PsiMethod method) {
|
||||
return hasReceiver(methodRef, method, methodRef.getFunctionalInterfaceType());
|
||||
}
|
||||
|
||||
private static boolean hasReceiver(@NotNull PsiMethodReferenceExpression methodRef,
|
||||
@NotNull PsiMethod method,
|
||||
PsiType functionalInterfaceType) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
final MethodSignature signature = interfaceMethod != null ? interfaceMethod.getSignature(LambdaUtil.getSubstitutor(interfaceMethod, resolveResult)) : null;
|
||||
LOG.assertTrue(signature != null);
|
||||
|
||||
+1
-6
@@ -24,7 +24,6 @@ import com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -110,11 +109,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.ourRefs.get();
|
||||
if (map == null) {
|
||||
map = new HashMap<PsiMethodReferenceExpression, PsiType>();
|
||||
PsiMethodReferenceUtil.ourRefs.set(map);
|
||||
}
|
||||
final Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.getFunctionalTypeMap();
|
||||
final PsiType added = map.put(myExpression, groundTargetType);
|
||||
final PsiElement resolve;
|
||||
try {
|
||||
|
||||
+1
-7
@@ -41,7 +41,6 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiMethodReferenceExpression {
|
||||
@@ -398,12 +397,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
|
||||
// the result of applying capture conversion (5.1.10) to the return type of the invocation type (15.12.2.6) of the chosen declaration is R',
|
||||
// where R is the target type that may be used to infer R'; neither R nor R' is void; and R' is compatible with R in an assignment context.
|
||||
|
||||
Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.ourRefs.get();
|
||||
if (map == null) {
|
||||
map = new HashMap<PsiMethodReferenceExpression, PsiType>();
|
||||
PsiMethodReferenceUtil.ourRefs.set(map);
|
||||
}
|
||||
|
||||
Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.getFunctionalTypeMap();
|
||||
final JavaResolveResult result;
|
||||
try {
|
||||
if (map.put(this, left) != null) {
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ interface I {
|
||||
public class Test {
|
||||
public int m() {
|
||||
I i = s -> {
|
||||
return s.contains();
|
||||
return s.isEmpty();
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ interface I {
|
||||
}
|
||||
public class Test {
|
||||
public int m() {
|
||||
I i = s -> s.contains();
|
||||
I i = s -> s.isEmpty();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class A {
|
||||
{
|
||||
List<A> s = new ArrayList<>();
|
||||
s.stream().filter(A::<caret>);
|
||||
}
|
||||
|
||||
public boolean accept(String s) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class A {
|
||||
{
|
||||
List<A> s = new ArrayList<>();
|
||||
s.stream().filter(A::accept);
|
||||
}
|
||||
|
||||
static <K> boolean accept(K k) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean accept1(String s) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class A {
|
||||
{
|
||||
List<A> s = new ArrayList<>();
|
||||
s.stream().filter(A::<caret>);
|
||||
}
|
||||
|
||||
static <K> boolean accept(K k) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean accept1(String s) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class A {
|
||||
{
|
||||
List<A> s = new ArrayList<>();
|
||||
s.stream().filter(A::accept);
|
||||
}
|
||||
|
||||
public boolean accept() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class A {
|
||||
{
|
||||
List<A> s = new ArrayList<>();
|
||||
s.stream().filter(A::<caret>);
|
||||
}
|
||||
|
||||
public boolean accept() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+28
-9
@@ -37,7 +37,7 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_LATEST;
|
||||
return JAVA_8;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,21 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase {
|
||||
}
|
||||
|
||||
public void testFilteredMethodReference() throws Exception {
|
||||
doTest();
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testFilteredStaticMethods() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testFilterWrongParamsMethods() throws Exception {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
public void testFilterAmbiguity() throws Exception {
|
||||
configureByFile("/" + getTestName(false) + ".java");
|
||||
assertNotNull(myItems);
|
||||
assertTrue(myItems.length == 0);
|
||||
}
|
||||
|
||||
public void testNotAvailableInLambdaPositionAfterQualifier() throws Exception {
|
||||
@@ -105,17 +119,22 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase {
|
||||
}
|
||||
|
||||
public void testDiamondsInsideMethodCall() throws Exception {
|
||||
configureByFile("/" + getTestName(false) + ".java");
|
||||
checkResultByFile("/" + getTestName(false) + "-out.java");
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doTest(true);
|
||||
}
|
||||
|
||||
private void doTest(boolean checkItems) {
|
||||
configureByFile("/" + getTestName(false) + ".java");
|
||||
assertNotNull(myItems);
|
||||
assertTrue(myItems.length > 0);
|
||||
final Lookup lookup = getLookup();
|
||||
if (lookup != null) {
|
||||
selectItem(lookup.getCurrentItem(), Lookup.NORMAL_SELECT_CHAR);
|
||||
if (checkItems) {
|
||||
assertNotNull(myItems);
|
||||
assertTrue(myItems.length > 0);
|
||||
final Lookup lookup = getLookup();
|
||||
if (lookup != null) {
|
||||
selectItem(lookup.getCurrentItem(), Lookup.NORMAL_SELECT_CHAR);
|
||||
}
|
||||
}
|
||||
checkResultByFile("/" + getTestName(false) + "-out.java");
|
||||
}
|
||||
|
||||
+12
@@ -19,6 +19,7 @@ import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ContentEntry;
|
||||
import com.intellij.openapi.roots.LanguageLevelModuleExtension;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
@@ -47,6 +48,17 @@ public abstract class LightCodeInsightFixtureTestCase extends UsefulTestCase{
|
||||
model.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(LanguageLevel.JDK_1_7);
|
||||
}
|
||||
};
|
||||
public static final LightProjectDescriptor JAVA_8 = new DefaultLightProjectDescriptor() {
|
||||
@Override
|
||||
public Sdk getSdk() {
|
||||
return IdeaTestUtil.getMockJdk18();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) {
|
||||
model.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(LanguageLevel.JDK_1_8);
|
||||
}
|
||||
};
|
||||
public static final LightProjectDescriptor JAVA_LATEST = new DefaultLightProjectDescriptor();
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user