Java: Filter by 'static' modifier in MethodHandle/VarHandle completion assistance (IDEA-167319, IDEA-CR-19610)

This commit is contained in:
Pavel Dolgov
2017-03-24 15:45:42 +03:00
parent 988087f02c
commit 25afffeb70
10 changed files with 47 additions and 28 deletions
@@ -161,7 +161,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
@NotNull String fieldName,
@NotNull PsiExpression fieldNameExpression,
@NotNull PsiExpression fieldTypeExpression,
boolean isStatic,
boolean isStaticExpected,
@NotNull PsiReferenceExpression factoryMethodExpression,
@NotNull ProblemsHolder holder) {
final PsiField field = ownerClass.findFieldByName(fieldName, true);
@@ -170,13 +170,13 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
return;
}
if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) {
if (field.hasModifierProperty(PsiModifier.STATIC) != isStaticExpected) {
final String factoryMethodName = factoryMethodExpression.getReferenceName();
final PsiElement factoryMethodNameElement = factoryMethodExpression.getReferenceNameElement();
if (factoryMethodName != null && factoryMethodNameElement != null) {
final LocalQuickFix fix = SwitchStaticnessQuickFix.createFix(factoryMethodName, isStatic);
final LocalQuickFix fix = SwitchStaticnessQuickFix.createFix(factoryMethodName, isStaticExpected);
final String message = InspectionsBundle.message(
isStatic ? "inspection.handle.signature.field.static" : "inspection.handle.signature.field.not.static", fieldName);
isStaticExpected ? "inspection.handle.signature.field.not.static" : "inspection.handle.signature.field.static", fieldName);
holder.registerProblem(factoryMethodNameElement, message, fix);
return;
}
@@ -196,7 +196,7 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
@NotNull String methodName,
@NotNull PsiExpression methodNameExpression,
@NotNull PsiExpression methodTypeExpression,
boolean isStatic,
boolean isStaticExpected,
@NotNull PsiReferenceExpression factoryMethodExpression,
@NotNull ProblemsHolder holder) {
@@ -207,14 +207,14 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
}
final List<PsiMethod> filteredMethods =
ContainerUtil.filter(methods, method -> method.hasModifierProperty(PsiModifier.STATIC) == isStatic);
ContainerUtil.filter(methods, method -> method.hasModifierProperty(PsiModifier.STATIC) == isStaticExpected);
if (filteredMethods.isEmpty()) {
final String factoryMethodName = factoryMethodExpression.getReferenceName();
final PsiElement factoryMethodNameElement = factoryMethodExpression.getReferenceNameElement();
if (factoryMethodName != null && factoryMethodNameElement != null) {
final LocalQuickFix fix = SwitchStaticnessQuickFix.createFix(factoryMethodName, isStatic);
final LocalQuickFix fix = SwitchStaticnessQuickFix.createFix(factoryMethodName, isStaticExpected);
final String message = InspectionsBundle.message(
isStatic ? "inspection.handle.signature.method.static" : "inspection.handle.signature.method.not.static", methodName);
isStaticExpected ? "inspection.handle.signature.method.not.static" : "inspection.handle.signature.method.static", methodName);
holder.registerProblem(factoryMethodNameElement, message, fix);
return;
}
@@ -392,8 +392,8 @@ public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalI
}
@Nullable
public static LocalQuickFix createFix(@NotNull String methodName, boolean isStatic) {
final String replacementName = isStatic ? STATIC_TO_NON_STATIC.get(methodName) : NON_STATIC_TO_STATIC.get(methodName);
public static LocalQuickFix createFix(@NotNull String methodName, boolean wasStatic) {
final String replacementName = wasStatic ? STATIC_TO_NON_STATIC.get(methodName) : NON_STATIC_TO_STATIC.get(methodName);
return replacementName != null ? new SwitchStaticnessQuickFix(replacementName) : null;
}
}
@@ -32,6 +32,7 @@ import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.intellij.codeInsight.completion.JavaCompletionContributor.isInJavaContext;
import static com.intellij.patterns.PsiJavaPatterns.*;
@@ -105,7 +106,7 @@ public class JavaMethodHandleCompletionContributor extends CompletionContributor
case FIND_SPECIAL:
final String name = arguments.length > 1 ? computeConstantExpression(arguments[1], String.class) : null;
if (!StringUtil.isEmpty(name)) {
addMethodSignatures(psiClass, name, result);
addMethodSignatures(psiClass, name, FIND_STATIC.equals(methodName), result);
}
break;
}
@@ -119,7 +120,7 @@ public class JavaMethodHandleCompletionContributor extends CompletionContributor
if (className != null) {
final PsiMethod[] constructors = psiClass.getConstructors();
if (constructors.length != 0) {
lookupMethodTypes(constructors, result);
lookupMethodTypes(Arrays.stream(constructors), result);
}
else {
result.addElement(lookupSignature(ReflectiveSignature.NO_ARGUMENT_CONSTRUCTOR_SIGNATURE));
@@ -127,15 +128,20 @@ public class JavaMethodHandleCompletionContributor extends CompletionContributor
}
}
private static void addMethodSignatures(@NotNull PsiClass psiClass, @NotNull String methodName, @NotNull CompletionResultSet result) {
private static void addMethodSignatures(@NotNull PsiClass psiClass,
@NotNull String methodName,
boolean isStaticExpected,
@NotNull CompletionResultSet result) {
final PsiMethod[] methods = psiClass.findMethodsByName(methodName, false);
if (methods.length != 0) {
lookupMethodTypes(methods, result);
final Stream<PsiMethod> methodStream = Arrays.stream(methods)
.filter(method -> method.hasModifierProperty(PsiModifier.STATIC) == isStaticExpected);
lookupMethodTypes(methodStream, result);
}
}
private static void lookupMethodTypes(@NotNull PsiMethod[] methods, @NotNull CompletionResultSet result) {
Arrays.stream(methods)
private static void lookupMethodTypes(@NotNull Stream<PsiMethod> methods, @NotNull CompletionResultSet result) {
methods
.map(JavaReflectionReferenceUtil::getMethodSignature)
.filter(Objects::nonNull)
.sorted(ReflectiveSignature::compareTo)
@@ -3,6 +3,6 @@ import java.lang.invoke.*;
public class Main {
void foo() throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
lookup.findStatic(Types.class, "sObjMethod", <caret>);
lookup.findStatic(Types.class, "objMethod", <caret>);
}
}
@@ -3,6 +3,6 @@ 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));
lookup.findStatic(Types.class, "objMethod", MethodType.methodType(Object.class, Object.class));
}
}
@@ -27,6 +27,9 @@ class Main {
l.findStaticGetter(Test.class, "ourInt", <warning descr="The type of field 'ourInt' is 'int'">void.class</warning>);
l.findStaticGetter(Test.class, "ourInts", <warning descr="The type of field 'ourInts' is 'int[]'">int.class</warning>);
l.findStaticGetter(Test.class, "ourString", <warning descr="The type of field 'ourString' is 'java.lang.String'">List.class</warning>);
l.<warning descr="Field 'ourString' is static">findGetter</warning>(Test.class, "ourString", String.class);
l.<warning descr="Field 'myString' is not static">findStaticGetter</warning>(Test.class, "myString", String.class);
}
}
@@ -23,7 +23,7 @@ class Main {
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'Object[] method(Object[])'">MethodType.methodType(Object[].class, Object[].class)</warning>);
l.findVirtual(Test.class, "method", <warning descr="Cannot resolve method 'Object[][] method(Object[][])'">MethodType.methodType(Object[][].class, Object[][].class)</warning>);
l.<warning descr="Method 'method' is static">findStatic</warning>(Test.class, "method", MethodType.methodType(void.class));
l.<warning descr="Method 'method' is not static">findStatic</warning>(Test.class, "method", MethodType.methodType(void.class));
l.findVirtual(Test.class, <warning descr="Cannot resolve method 'doesntExist'">"doesntExist"</warning>, MethodType.methodType(void.class));
}
}
@@ -27,6 +27,9 @@ class Main {
l.findStaticSetter(Test.class, "ourInt", <warning descr="The type of field 'ourInt' is 'int'">void.class</warning>);
l.findStaticSetter(Test.class, "ourInts", <warning descr="The type of field 'ourInts' is 'int[]'">int.class</warning>);
l.findStaticSetter(Test.class, "ourString", <warning descr="The type of field 'ourString' is 'java.lang.String'">List.class</warning>);
l.<warning descr="Field 'myString' is not static">findStaticSetter</warning>(Test.class, "myString", String.class);
l.<warning descr="Field 'ourString' is static">findSetter</warning>(Test.class, "ourString", String.class);
}
}
@@ -12,7 +12,7 @@ class Main {
l.findStatic(Test.class, "method2", <warning descr="Cannot resolve method 'int method2(String)'">MethodType.methodType(int.class, String.class)</warning>);
l.findStatic(Test.class, "method3", <warning descr="Cannot resolve method 'String method3()'">MethodType.methodType(String.class)</warning>);
l.<warning descr="Method 'method1' is not static">findVirtual</warning>(Test.class, "method1", MethodType.methodType(void.class));
l.<warning descr="Method 'method1' is static">findVirtual</warning>(Test.class, "method1", MethodType.methodType(void.class));
l.findStatic(Test.class, <warning descr="Cannot resolve method 'doesntExist'">"doesntExist"</warning>, MethodType.methodType(String.class));
}
}
@@ -27,6 +27,9 @@ class Main {
l.findStaticVarHandle(Test.class, "ourInt", <warning descr="The type of field 'ourInt' is 'int'">void.class</warning>);
l.findStaticVarHandle(Test.class, "ourInts", <warning descr="The type of field 'ourInts' is 'int[]'">int.class</warning>);
l.findStaticVarHandle(Test.class, "ourString", <warning descr="The type of field 'ourString' is 'java.lang.String'">List.class</warning>);
l.<warning descr="Field 'myString' is not static">findStaticVarHandle</warning>(Test.class, "myString", String.class);
l.<warning descr="Field 'ourString' is static">findVarHandle</warning>(Test.class, "ourString", String.class);
}
}
@@ -45,8 +45,10 @@ class JavaLangInvokeHandleCompletionTest : LightFixtureCompletionTestCase() {
fun testStaticVarHandle() = doTest(0, "psf1", "sf1", "sf2")
fun testVirtualType() = doTestTypes(0, "MethodType.methodType(String.class)")
fun testStaticType() = doTestTypes(0, "MethodType.methodType(Object.class, Object.class)")
fun testVirtualType() = doTestTypes(0, "MethodType.methodType(String.class)", "MethodType.methodType(String.class, int.class, int.class)")
fun testStaticType() = doTestTypes(1,
"MethodType.methodType(Object.class, int.class, int.class)",
"MethodType.methodType(Object.class, Object.class)")
fun testGetterType() = doTestTypes(0, "int.class")
fun testSetterType() = doTestTypes(0, "float.class")
@@ -86,7 +88,13 @@ public class Types extends Parent {
static Object sObj;
String strMethod() {return "";}
static Object sObjMethod(Object o) {return this;}
String strMethod(int n, int m) {return "";}
static String strMethod(int n) {return "";}
static Object objMethod(Object o) {return o;}
static Object objMethod(int n, int m) {return n;}
Object objMethod(int n) {return n;}
<T> T genericMethod(T t, String s) {return t;}
static <T> T sGenericMethod(List<T> lst, T... ts) {return ts[0];}
}""")
@@ -105,11 +113,7 @@ public class Constructed<T> {
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 lookupTexts = elements!!.map { LookupElementPresentation.renderElement(it).itemText }
val actual = if (compareFirst) lookupTexts.subList(0, Math.min(expected.size, lookupTexts.size)) else lookupTexts
assertOrderedEquals(actual, *expected)