mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: Implemented completion of field types and method signatures in calls to findConstructor(), findVirtual(), findVarHandle(), etc (IDEA-167319)
This commit is contained in:
+220
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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.psi.impl.source.resolve.reference.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.patterns.PsiJavaElementPattern;
|
||||
import com.intellij.patterns.PsiMethodPattern;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.codeInsight.completion.JavaCompletionContributor.isInJavaContext;
|
||||
import static com.intellij.patterns.PsiJavaPatterns.*;
|
||||
import static com.intellij.patterns.StandardPatterns.or;
|
||||
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*;
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
public class JavaMethodHandleCompletionContributor extends CompletionContributor {
|
||||
|
||||
// MethodHandle for constructors and methods
|
||||
private static final Set<String> METHOD_HANDLE_FACTORY_NAMES = ContainerUtil.immutableSet(
|
||||
FIND_CONSTRUCTOR, FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL);
|
||||
|
||||
private static final PsiJavaElementPattern.Capture<PsiElement> METHOD_TYPE_ARGUMENT_PATTERN = psiElement().afterLeaf(",")
|
||||
.withParent(or(
|
||||
psiExpression().methodCallParameter(1, methodPattern(FIND_CONSTRUCTOR)),
|
||||
psiExpression().methodCallParameter(2, methodPattern(FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL))));
|
||||
|
||||
|
||||
// VarHandle for fields and synthetic MethodHandle for field getters/setters
|
||||
private static final Set<String> FIELD_HANDLE_FACTORY_NAMES = ContainerUtil.immutableSet(
|
||||
FIND_GETTER, FIND_SETTER, FIND_STATIC_GETTER, FIND_STATIC_SETTER, FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE);
|
||||
|
||||
private static final PsiJavaElementPattern.Capture<PsiElement> FIELD_TYPE_ARGUMENT_PATTERN = psiElement().afterLeaf(",")
|
||||
.withParent(
|
||||
psiExpression().methodCallParameter(2, methodPattern(ArrayUtil.toStringArray(FIELD_HANDLE_FACTORY_NAMES))));
|
||||
|
||||
|
||||
@NotNull
|
||||
private static PsiMethodPattern methodPattern(String... methodNames) {
|
||||
return psiMethod().withName(methodNames).definedInClass(JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
|
||||
if (parameters.getCompletionType() != CompletionType.BASIC) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiElement position = parameters.getPosition();
|
||||
if (!isInJavaContext(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (METHOD_TYPE_ARGUMENT_PATTERN.accepts(position)) {
|
||||
addMethodHandleVariants(position, result);
|
||||
}
|
||||
else if (FIELD_TYPE_ARGUMENT_PATTERN.accepts(position)) {
|
||||
addFieldHandleVariants(position, result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addMethodHandleVariants(@NotNull PsiElement position, @NotNull CompletionResultSet result) {
|
||||
final PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class);
|
||||
if (methodCall != null) {
|
||||
final String methodName = methodCall.getMethodExpression().getReferenceName();
|
||||
if (methodName != null && METHOD_HANDLE_FACTORY_NAMES.contains(methodName)) {
|
||||
final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
|
||||
final PsiClass psiClass = arguments.length != 0 ? getReflectiveClass(arguments[0]) : null;
|
||||
if (psiClass != null) {
|
||||
|
||||
switch (methodName) {
|
||||
case FIND_CONSTRUCTOR:
|
||||
addConstructorSignatures(psiClass, result);
|
||||
break;
|
||||
|
||||
case FIND_VIRTUAL:
|
||||
case FIND_STATIC:
|
||||
case FIND_SPECIAL:
|
||||
final String name = arguments.length > 1 ? computeConstantExpression(arguments[1], String.class) : null;
|
||||
if (!StringUtil.isEmpty(name)) {
|
||||
addMethodSignatures(psiClass, name, result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addConstructorSignatures(@NotNull PsiClass psiClass, @NotNull CompletionResultSet result) {
|
||||
final String className = psiClass.getName();
|
||||
if (className != null) {
|
||||
final PsiMethod[] constructors = psiClass.getConstructors();
|
||||
if (constructors.length != 0) {
|
||||
lookupMethodTypes(constructors, result);
|
||||
}
|
||||
else {
|
||||
result.addElement(lookupSignature(ReflectiveSignature.NO_ARGUMENT_CONSTRUCTOR_SIGNATURE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addMethodSignatures(@NotNull PsiClass psiClass, @NotNull String methodName, @NotNull CompletionResultSet result) {
|
||||
final PsiMethod[] methods = psiClass.findMethodsByName(methodName, false);
|
||||
if (methods.length != 0) {
|
||||
lookupMethodTypes(methods, result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void lookupMethodTypes(@NotNull PsiMethod[] methods, @NotNull CompletionResultSet result) {
|
||||
Arrays.stream(methods)
|
||||
.map(JavaReflectionReferenceUtil::getMethodSignature)
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(ReflectiveSignature::compareTo)
|
||||
.map(JavaMethodHandleCompletionContributor::lookupSignature)
|
||||
.forEach(result::addElement);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static LookupElement lookupSignature(@NotNull ReflectiveSignature signature) {
|
||||
final String types = signature.stream()
|
||||
.map(text -> PsiNameHelper.getShortClassName(text) + ".class")
|
||||
.collect(Collectors.joining(", "));
|
||||
final String text = PsiNameHelper.getShortClassName(JAVA_LANG_INVOKE_METHOD_TYPE) + "." + METHOD_TYPE + "(" + types + ")";
|
||||
|
||||
final LookupElementBuilder element = LookupElementBuilder
|
||||
.create(signature, "")
|
||||
.withPresentableText(text)
|
||||
.withIcon(PlatformIcons.METHOD_ICON)
|
||||
.withInsertHandler(JavaMethodHandleCompletionContributor::handleInsertMethodType);
|
||||
|
||||
return PrioritizedLookupElement.withPriority(element, 1);
|
||||
}
|
||||
|
||||
private static void handleInsertMethodType(InsertionContext context, LookupElement item) {
|
||||
final Object object = item.getObject();
|
||||
if (object instanceof ReflectiveSignature) {
|
||||
final String text = getMethodTypeExpressionText((ReflectiveSignature)object);
|
||||
replaceText(context, text);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addFieldHandleVariants(@NotNull PsiElement position, @NotNull CompletionResultSet result) {
|
||||
final PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class);
|
||||
if (methodCall != null) {
|
||||
final String methodName = methodCall.getMethodExpression().getReferenceName();
|
||||
if (methodName != null && FIELD_HANDLE_FACTORY_NAMES.contains(methodName)) {
|
||||
final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
|
||||
if (arguments.length > 2) {
|
||||
final String fieldName = computeConstantExpression(arguments[1], String.class);
|
||||
if (!StringUtil.isEmpty(fieldName)) {
|
||||
final PsiClass psiClass = getReflectiveClass(arguments[0]);
|
||||
if (psiClass != null) {
|
||||
addFieldType(psiClass, fieldName, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addFieldType(@NotNull PsiClass psiClass, @NotNull String fieldName, @NotNull CompletionResultSet result) {
|
||||
final PsiField field = psiClass.findFieldByName(fieldName, false);
|
||||
if (field != null) {
|
||||
final String typeText = getTypeText(field.getType(), field);
|
||||
if (typeText != null) {
|
||||
final LookupElementBuilder element = LookupElementBuilder
|
||||
.create(new TypeLiteral(typeText), "")
|
||||
.withPresentableText(PsiNameHelper.getShortClassName(typeText) + ".class")
|
||||
.withIcon(PlatformIcons.CLASS_ICON)
|
||||
.withInsertHandler(JavaMethodHandleCompletionContributor::handleInsertFieldType);
|
||||
result.addElement(PrioritizedLookupElement.withPriority(element, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleInsertFieldType(InsertionContext context, LookupElement item) {
|
||||
final Object object = item.getObject();
|
||||
if (object instanceof TypeLiteral) {
|
||||
final String text = ((TypeLiteral)object).getText();
|
||||
replaceText(context, text);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TypeLiteral {
|
||||
private final String myType;
|
||||
|
||||
TypeLiteral(@NotNull String type) {myType = type;}
|
||||
|
||||
@NotNull
|
||||
String getText() {return myType + ".class";}
|
||||
}
|
||||
}
|
||||
+17
-19
@@ -49,6 +49,7 @@ import java.util.stream.Stream;
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
public class JavaReflectionReferenceUtil {
|
||||
// MethodHandle (Java 7) and VarHandle (Java 9) infrastructure
|
||||
public static final String JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP = "java.lang.invoke.MethodHandles.Lookup";
|
||||
public static final String JAVA_LANG_INVOKE_METHOD_TYPE = "java.lang.invoke.MethodType";
|
||||
|
||||
@@ -67,6 +68,15 @@ public class JavaReflectionReferenceUtil {
|
||||
public static final String FIND_VAR_HANDLE = "findVarHandle";
|
||||
public static final String FIND_STATIC_VAR_HANDLE = "findStaticVarHandle";
|
||||
|
||||
public static final String FIND_CONSTRUCTOR = "findConstructor";
|
||||
|
||||
public static final String[] HANDLE_FACTORY_METHOD_NAMES = {
|
||||
FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL,
|
||||
FIND_GETTER, FIND_SETTER,
|
||||
FIND_STATIC_GETTER, FIND_STATIC_SETTER,
|
||||
FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE};
|
||||
|
||||
// Classic reflection infrastructure
|
||||
public static final String GET_FIELD = "getField";
|
||||
public static final String GET_DECLARED_FIELD = "getDeclaredField";
|
||||
public static final String GET_METHOD = "getMethod";
|
||||
@@ -77,12 +87,6 @@ public class JavaReflectionReferenceUtil {
|
||||
public static final String LOAD_CLASS = "loadClass";
|
||||
public static final String GET_CLASS = "getClass";
|
||||
|
||||
public static final String[] HANDLE_FACTORY_METHOD_NAMES = {
|
||||
FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL,
|
||||
FIND_GETTER, FIND_SETTER,
|
||||
FIND_STATIC_GETTER, FIND_STATIC_SETTER,
|
||||
FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE};
|
||||
|
||||
private static final RecursionGuard ourGuard = RecursionManager.createGuard("JavaLangClassMemberReference");
|
||||
|
||||
@Nullable
|
||||
@@ -230,10 +234,10 @@ public class JavaReflectionReferenceUtil {
|
||||
}
|
||||
|
||||
static void shortenArgumentsClassReferences(@NotNull InsertionContext context) {
|
||||
final PsiElement firstParam = PsiUtilCore.getElementAtOffset(context.getFile(), context.getStartOffset());
|
||||
final PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(firstParam, PsiMethodCallExpression.class);
|
||||
if (methodCall != null) {
|
||||
JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(methodCall.getArgumentList());
|
||||
final PsiElement parameter = PsiUtilCore.getElementAtOffset(context.getFile(), context.getStartOffset());
|
||||
final PsiExpressionList parameterList = PsiTreeUtil.getParentOfType(parameter, PsiExpressionList.class);
|
||||
if (parameterList != null && parameterList.getParent() instanceof PsiMethodCallExpression) {
|
||||
JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(parameterList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,9 +268,9 @@ public class JavaReflectionReferenceUtil {
|
||||
|
||||
static void replaceText(@NotNull InsertionContext context, @NotNull String text) {
|
||||
final PsiElement newElement = PsiUtilCore.getElementAtOffset(context.getFile(), context.getStartOffset());
|
||||
final int start = newElement.getTextRange().getEndOffset();
|
||||
final PsiElement params = newElement.getParent().getParent();
|
||||
final int end = params.getTextRange().getEndOffset() - 1;
|
||||
final int start = Math.min(newElement.getTextRange().getEndOffset(), end);
|
||||
|
||||
context.getDocument().replaceString(start, end, text);
|
||||
context.commitDocument();
|
||||
@@ -429,24 +433,18 @@ public class JavaReflectionReferenceUtil {
|
||||
|
||||
@NotNull
|
||||
public String getShortReturnType() {
|
||||
return getShortTypeText(myReturnType);
|
||||
return PsiNameHelper.getShortClassName(myReturnType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getShortArgumentTypes() {
|
||||
final StringJoiner joiner = new StringJoiner(", ", "(", ")");
|
||||
for (String argumentType : myArgumentTypes) {
|
||||
joiner.add(getShortTypeText(argumentType));
|
||||
joiner.add(PsiNameHelper.getShortClassName(argumentType));
|
||||
}
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getShortTypeText(String text) {
|
||||
final int pos = text.lastIndexOf('.');
|
||||
return pos < 0 ? text : text.substring(pos + 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Icon getIcon() {
|
||||
return myIcon != null ? myIcon : PlatformIcons.METHOD_ICON;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findConstructor(Types.class, <caret>);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findConstructor(Types.class, MethodType.methodType(void.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findConstructor(Constructed.class, <caret>);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findConstructor(Constructed.class, MethodType.methodType(void.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findConstructor(Constructed.class, <caret>);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findConstructor(Constructed.class, MethodType.methodType(void.class, Object[].class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findGetter(Test.class, "f1",<caret>);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findGetter(Test.class, "f1",int.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findSetter(Test.class, "f2", <caret> String.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findSetter(Test.class, "f2", float.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStatic(Types.class, "sObjMethod", <caret>);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStatic(Types.class, "sGenericMethod", <caret>);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import java.lang.invoke.*;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStatic(Types.class, "sGenericMethod", MethodType.methodType(Object.class, List.class, Object[].class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStatic(Types.class, "sObjMethod", MethodType.methodType(Object.class, Object.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticVarHandle(Types.class, "sObj", <caret>);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findStaticVarHandle(Types.class, "sObj", Object.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVarHandle(Types.class, "str",<caret>);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVarHandle(Types.class, "str",String.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVirtual(Types.class, "strMethod",<caret>);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVirtual(Types.class, "genericMethod", <caret>);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVirtual(Types.class, "genericMethod", MethodType.methodType(Object.class, Object.class, String.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.lang.invoke.*;
|
||||
|
||||
public class Main {
|
||||
void foo() throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
lookup.findVirtual(Types.class, "strMethod", MethodType.methodType(String.class));
|
||||
}
|
||||
}
|
||||
+68
-8
@@ -16,6 +16,7 @@
|
||||
package com.intellij.codeInsight.completion
|
||||
|
||||
import com.intellij.JavaTestUtil
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
|
||||
@@ -24,9 +25,10 @@ import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
*/
|
||||
class JavaLangInvokeHandleCompletionTest : LightFixtureCompletionTestCase() {
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor {
|
||||
return LightCodeInsightFixtureTestCase.JAVA_9
|
||||
}
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = LightCodeInsightFixtureTestCase.JAVA_9
|
||||
|
||||
override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/completion/invokeHandle/"
|
||||
|
||||
|
||||
fun testVirtual() = doTestFirst(1, "m1", "pm1", "m2")
|
||||
fun testVirtualPrefixed() = doTest(1, "m1", "m2", "pm1")
|
||||
@@ -42,17 +44,75 @@ class JavaLangInvokeHandleCompletionTest : LightFixtureCompletionTestCase() {
|
||||
fun testVarHandle() = doTest(0, "f1", "pf1", "f2")
|
||||
fun testStaticVarHandle() = doTest(0, "psf1", "sf1", "sf2")
|
||||
|
||||
override fun getBasePath(): String {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/completion/invokeHandle/"
|
||||
}
|
||||
|
||||
fun testVirtualType() = doTestTypes(0, "MethodType.methodType(String.class)")
|
||||
fun testStaticType() = doTestTypes(0, "MethodType.methodType(Object.class, Object.class)")
|
||||
|
||||
fun testGetterType() = doTestTypes(0, "int.class")
|
||||
fun testSetterType() = doTestTypes(0, "float.class")
|
||||
|
||||
fun testVarHandleType() = doTestTypes(0, "String.class")
|
||||
fun testStaticVarHandleType() = doTestTypes(0, "Object.class")
|
||||
|
||||
fun testVirtualTypeGeneric() = doTestTypes(0, "MethodType.methodType(Object.class, Object.class, String.class)")
|
||||
fun testStaticTypeGeneric() = doTestTypes(0, "MethodType.methodType(Object.class, List.class, Object[].class)")
|
||||
|
||||
fun testConstructorType1() = doTestTypes(0, "MethodType.methodType(void.class)")
|
||||
fun testConstructorType2() = doTestTypes(0,
|
||||
"MethodType.methodType(void.class)",
|
||||
"MethodType.methodType(void.class, int.class)",
|
||||
"MethodType.methodType(void.class, List.class)",
|
||||
"MethodType.methodType(void.class, Object[].class)")
|
||||
fun testConstructorType3() = doTestTypes(3,
|
||||
"MethodType.methodType(void.class)",
|
||||
"MethodType.methodType(void.class, int.class)",
|
||||
"MethodType.methodType(void.class, List.class)",
|
||||
"MethodType.methodType(void.class, Object[].class)")
|
||||
|
||||
|
||||
private fun doTest(index: Int, vararg expected: String) {
|
||||
doTest(index, { assertStringItems(*expected) })
|
||||
doTest(index, { assertLookupTexts(false, *expected) })
|
||||
}
|
||||
|
||||
private fun doTestFirst(index: Int, vararg expected: String) {
|
||||
doTest(index, { assertFirstStringItems(*expected, "clone") })
|
||||
doTest(index, { assertLookupTexts(true, *expected, "clone") })
|
||||
}
|
||||
|
||||
private fun doTestTypes(index: Int, vararg expected: String) {
|
||||
myFixture.addClass("""
|
||||
import java.util.List;
|
||||
public class Types extends Parent {
|
||||
String str;
|
||||
static Object sObj;
|
||||
|
||||
String strMethod() {return "";}
|
||||
static Object sObjMethod(Object o) {return this;}
|
||||
<T> T genericMethod(T t, String s) {return t;}
|
||||
static <T> T sGenericMethod(List<T> lst, T... ts) {return ts[0];}
|
||||
}""")
|
||||
myFixture.addClass("""
|
||||
import java.util.List;
|
||||
public class Constructed<T> {
|
||||
Constructed() {}
|
||||
Constructed(int n) {}
|
||||
Constructed(List<T> a) {}
|
||||
Constructed(T... a) {}
|
||||
}""")
|
||||
|
||||
doTest(index, { assertLookupTexts(true, *expected) })
|
||||
}
|
||||
|
||||
private fun assertLookupTexts(compareFirst: Boolean, vararg expected: String) {
|
||||
val elements = myFixture.lookupElements
|
||||
assertNotNull(elements)
|
||||
val lookupTexts = elements!!.map {
|
||||
val presentation = LookupElementPresentation()
|
||||
it.renderElement(presentation)
|
||||
presentation.itemText
|
||||
}
|
||||
|
||||
val actual = if (compareFirst) lookupTexts.subList(0, Math.min(expected.size, lookupTexts.size)) else lookupTexts
|
||||
assertOrderedEquals(actual, *expected)
|
||||
}
|
||||
|
||||
private fun doTest(index: Int, assertion: () -> Unit) {
|
||||
|
||||
@@ -422,6 +422,8 @@
|
||||
order="last, before default"/>
|
||||
<completion.contributor language="JAVA" implementationClass="com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionCompletionContributor" id="javaReflection"
|
||||
order="last, before javaLegacy"/>
|
||||
<completion.contributor language="JAVA" implementationClass="com.intellij.psi.impl.source.resolve.reference.impl.JavaMethodHandleCompletionContributor" id="javaMethodHandle"
|
||||
order="last, before javaLegacy"/>
|
||||
|
||||
<lookup.charFilter implementation="com.intellij.codeInsight.completion.JavaCharFilter" id="java"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user