mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
Java: Improved completion for arguments of getField() and getMethod() - extract the type information from forName() and getClass() to provide code assistance (IDEA-167250)
This commit is contained in:
@@ -18,7 +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.completion.JavaLookupElementBuilder;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -26,12 +25,14 @@ import com.intellij.openapi.util.RecursionGuard;
|
||||
import com.intellij.openapi.util.RecursionManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.DeclarationSearchUtils;
|
||||
import com.siyeh.ig.psiutils.ParenthesesUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -41,10 +42,15 @@ import java.util.Arrays;
|
||||
* @author Konstantin Bulenkov
|
||||
*/
|
||||
public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExpression> implements InsertHandler<LookupElement> {
|
||||
private static final String FIELD = "getField";
|
||||
private static final String DECLARED_FIELD = "getDeclaredField";
|
||||
private static final String METHOD = "getMethod";
|
||||
private static final String DECLARED_METHOD = "getDeclaredMethod";
|
||||
|
||||
private static final RecursionGuard ourGuard = RecursionManager.createGuard("JavaLangClassMemberReference");
|
||||
private final PsiExpression myContext;
|
||||
|
||||
public JavaLangClassMemberReference(PsiLiteralExpression literal, PsiExpression context) {
|
||||
public JavaLangClassMemberReference(@NotNull PsiLiteralExpression literal, @NotNull PsiExpression context) {
|
||||
super(literal);
|
||||
myContext = context;
|
||||
}
|
||||
@@ -56,30 +62,38 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
final String name = (String)getElement().getValue();
|
||||
final Type type = getType();
|
||||
Object value = myElement.getValue();
|
||||
if (value instanceof String) {
|
||||
final String name = (String)value;
|
||||
final String type = getMemberType();
|
||||
|
||||
if (type != null) {
|
||||
final PsiClass psiClass = getPsiClass();
|
||||
if (psiClass != null) {
|
||||
switch (type) {
|
||||
if (type != null) {
|
||||
final PsiClass psiClass = getPsiClass();
|
||||
if (psiClass != null) {
|
||||
switch (type) {
|
||||
|
||||
case FIELD: {
|
||||
PsiField field = psiClass.findFieldByName(name, true);
|
||||
return isPublic(field) ? field : null;
|
||||
}
|
||||
case FIELD: {
|
||||
return psiClass.findFieldByName(name, true);
|
||||
}
|
||||
|
||||
case DECLARED_FIELD:
|
||||
return psiClass.findFieldByName(name, false);
|
||||
case DECLARED_FIELD: {
|
||||
PsiField field = psiClass.findFieldByName(name, false);
|
||||
return isReachable(field, psiClass) ? field : null;
|
||||
}
|
||||
|
||||
case METHOD: {
|
||||
final PsiMethod[] methods = psiClass.findMethodsByName(name, true);
|
||||
return ContainerUtil.find(methods, JavaLangClassMemberReference::isPublic);
|
||||
}
|
||||
case METHOD: {
|
||||
final PsiMethod[] methods = psiClass.findMethodsByName(name, true);
|
||||
final PsiMethod publicMethod = ContainerUtil.find(methods, method -> isRegularMethod(method) && isPublic(method));
|
||||
if (publicMethod != null) {
|
||||
return publicMethod;
|
||||
}
|
||||
return ContainerUtil.find(methods, method -> isRegularMethod(method));
|
||||
}
|
||||
|
||||
case DECLARED_METHOD: {
|
||||
final PsiMethod[] methods = psiClass.findMethodsByName(name, false);
|
||||
return methods.length == 0 ? null : methods[0];
|
||||
case DECLARED_METHOD: {
|
||||
final PsiMethod[] methods = psiClass.findMethodsByName(name, false);
|
||||
return ContainerUtil.find(methods, method -> isRegularMethod(method) && isReachable(method, psiClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,88 +114,88 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
return PsiTypesUtil.getPsiClass(operand.getType());
|
||||
}
|
||||
|
||||
PsiType type = context.getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
|
||||
if (!isJavaLangClass(resolveResult.getElement())) return null;
|
||||
PsiTypeParameter[] parameters = resolveResult.getElement().getTypeParameters();
|
||||
if (parameters.length == 1) {
|
||||
PsiType typeArgument = resolveResult.getSubstitutor().substitute(parameters[0]);
|
||||
PsiClass argumentClass = PsiTypesUtil.getPsiClass(typeArgument);
|
||||
if (argumentClass != null) return argumentClass;
|
||||
}
|
||||
}
|
||||
if (context instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)context;
|
||||
if ("forName".equals(methodCall.getMethodExpression().getReferenceName())) {
|
||||
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)context;
|
||||
final String methodReferenceName = methodCall.getMethodExpression().getReferenceName();
|
||||
if ("forName".equals(methodReferenceName)) {
|
||||
final PsiMethod method = methodCall.resolveMethod();
|
||||
if (method != null && isJavaLangClass(method.getContainingClass())) {
|
||||
final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions();
|
||||
if (expressions.length == 1 && expressions[0] instanceof PsiLiteralExpression) {
|
||||
final Object value = ((PsiLiteralExpression)expressions[0]).getValue();
|
||||
if (expressions.length == 1) {
|
||||
PsiExpression argument = ParenthesesUtils.stripParentheses(expressions[0]);
|
||||
if (argument instanceof PsiReferenceExpression) {
|
||||
argument = findVariableDefinition(((PsiReferenceExpression)argument));
|
||||
}
|
||||
final Object value = JavaConstantExpressionEvaluator.computeConstantExpression(argument, false);
|
||||
if (value instanceof String) {
|
||||
final Project project = context.getProject();
|
||||
return JavaPsiFacade.getInstance(project).findClass(String.valueOf(value), GlobalSearchScope.allScope(project));
|
||||
return JavaPsiFacade.getInstance(project).findClass((String)value, GlobalSearchScope.allScope(project));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("getClass".equals(methodReferenceName) && methodCall.getArgumentList().getExpressions().length == 0) {
|
||||
final PsiMethod method = methodCall.resolveMethod();
|
||||
if (method != null && isJavaLangObject(method.getContainingClass())) {
|
||||
final PsiExpression qualifier = ParenthesesUtils.stripParentheses(methodCall.getMethodExpression().getQualifierExpression());
|
||||
if (qualifier instanceof PsiReferenceExpression) {
|
||||
final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)qualifier);
|
||||
if (definition != null) {
|
||||
final PsiClass actualClass = PsiTypesUtil.getPsiClass(definition.getType());
|
||||
if (actualClass != null) {
|
||||
return actualClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
//TODO type of the qualifier may be a supertype of the actual value - need to compute the type of the actual value
|
||||
// otherwise getDeclaredField and getDeclaredMethod may work not reliably
|
||||
if (qualifier != null) {
|
||||
return PsiTypesUtil.getPsiClass(qualifier.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiType type = context.getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
|
||||
if (!isJavaLangClass(resolveResult.getElement())) return null;
|
||||
final PsiTypeParameter[] parameters = resolveResult.getElement().getTypeParameters();
|
||||
if (parameters.length == 1) {
|
||||
PsiType typeArgument = resolveResult.getSubstitutor().substitute(parameters[0]);
|
||||
if (typeArgument instanceof PsiCapturedWildcardType) {
|
||||
typeArgument = ((PsiCapturedWildcardType)typeArgument).getUpperBound();
|
||||
}
|
||||
final PsiClass argumentClass = PsiTypesUtil.getPsiClass(typeArgument);
|
||||
if (argumentClass != null && !isJavaLangObject(argumentClass)) {
|
||||
return argumentClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (context instanceof PsiReferenceExpression) {
|
||||
PsiElement resolved = ((PsiReferenceExpression)context).resolve();
|
||||
final PsiElement resolved = ((PsiReferenceExpression)context).resolve();
|
||||
if (resolved instanceof PsiVariable) {
|
||||
PsiExpression initializer = getInitializer((PsiVariable)resolved, context);
|
||||
if (initializer != null) {
|
||||
return ourGuard.doPreventingRecursion(resolved, false, () -> getPsiClass(initializer));
|
||||
final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)context, (PsiVariable)resolved);
|
||||
if (definition != null) {
|
||||
return ourGuard.doPreventingRecursion(resolved, false, () -> getPsiClass(definition));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PsiExpression getInitializer(@NotNull PsiVariable variable, @NotNull PsiExpression usage) {
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
if (initializer != null) {
|
||||
if (variable.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
private static PsiExpression findVariableDefinition(@NotNull PsiReferenceExpression referenceExpression) {
|
||||
final PsiElement resolved = referenceExpression.resolve();
|
||||
return resolved instanceof PsiVariable ? findVariableDefinition(referenceExpression, (PsiVariable)resolved) : null;
|
||||
}
|
||||
|
||||
private static PsiExpression findVariableDefinition(@NotNull PsiReferenceExpression referenceExpression, @NotNull PsiVariable variable) {
|
||||
if (variable.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
final PsiExpression initializer = variable.getInitializer();
|
||||
if (initializer != null) {
|
||||
return initializer;
|
||||
}
|
||||
if (variable instanceof PsiLocalVariable) {
|
||||
PsiDeclarationStatement declarationStatement = ObjectUtils.tryCast(variable.getParent(), PsiDeclarationStatement.class);
|
||||
if (declarationStatement != null) {
|
||||
PsiStatement usageStatement = PsiTreeUtil.getParentOfType(usage, PsiStatement.class);
|
||||
if (PsiTreeUtil.getNextSiblingOfType(declarationStatement, PsiStatement.class) == usageStatement) {
|
||||
return initializer;
|
||||
}
|
||||
PsiElement scope = PsiUtil.getVariableCodeBlock(variable, usage);
|
||||
if (scope != null && HighlightControlFlowUtil.isEffectivelyFinal(variable, scope, null)) {
|
||||
return initializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiStatement usageStatement = PsiTreeUtil.getParentOfType(usage, PsiStatement.class);
|
||||
if (usageStatement != null) {
|
||||
return getAssignedVisibleInUsage(variable, usageStatement);
|
||||
}
|
||||
// TODO: handle other initializations and assignments where the class can be resolved unambiguously
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpression getAssignedVisibleInUsage(@NotNull PsiVariable variable, PsiStatement usageStatement) {
|
||||
PsiStatement previousStatement = PsiTreeUtil.getPrevSiblingOfType(usageStatement, PsiStatement.class);
|
||||
if (previousStatement instanceof PsiExpressionStatement) {
|
||||
PsiExpression expression = ((PsiExpressionStatement)previousStatement).getExpression();
|
||||
if (expression instanceof PsiAssignmentExpression &&
|
||||
JavaTokenType.EQ.equals(((PsiAssignmentExpression)expression).getOperationTokenType())) {
|
||||
PsiExpression lExpression = ((PsiAssignmentExpression)expression).getLExpression();
|
||||
lExpression = ParenthesesUtils.stripParentheses(lExpression);
|
||||
if (lExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)lExpression).resolve() == variable) {
|
||||
return ((PsiAssignmentExpression)expression).getRExpression();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return DeclarationSearchUtils.findDefinition(referenceExpression, variable);
|
||||
}
|
||||
|
||||
private static boolean isJavaLangClass(PsiClass aClass) {
|
||||
@@ -193,19 +207,15 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Type getType() {
|
||||
PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(myElement, PsiMethodCallExpression.class);
|
||||
if (methodCall != null) {
|
||||
String name = methodCall.getMethodExpression().getReferenceName();
|
||||
return Type.fromString(name);
|
||||
}
|
||||
return null;
|
||||
private String getMemberType() {
|
||||
final PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(myElement, PsiMethodCallExpression.class);
|
||||
return methodCall != null ? methodCall.getMethodExpression().getReferenceName() : null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] getVariants() {
|
||||
final Type type = getType();
|
||||
final String type = getMemberType();
|
||||
if (type != null) {
|
||||
final PsiClass psiClass = getPsiClass();
|
||||
if (psiClass != null) {
|
||||
@@ -215,17 +225,17 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
return psiClass.getFields();
|
||||
|
||||
case FIELD:
|
||||
return ContainerUtil.filter(psiClass.getAllFields(), JavaLangClassMemberReference::isPublic).toArray();
|
||||
return ContainerUtil.filter(psiClass.getAllFields(), field -> isReachable(field, psiClass)).toArray();
|
||||
|
||||
case DECLARED_METHOD:
|
||||
return Arrays.stream(psiClass.getMethods())
|
||||
.filter(method -> !method.isConstructor())
|
||||
.filter(method -> isRegularMethod(method))
|
||||
.map(this::lookupMethod)
|
||||
.toArray();
|
||||
|
||||
case METHOD:
|
||||
return Arrays.stream(psiClass.getAllMethods())
|
||||
.filter(method -> isPublic(method) && !method.isConstructor() && !isJavaLangObject(method.getContainingClass()))
|
||||
.filter(method -> isRegularMethod(method) && isReachable(method, psiClass) && !isJavaLangObject(method.getContainingClass()))
|
||||
.map(this::lookupMethod)
|
||||
.toArray();
|
||||
}
|
||||
@@ -258,11 +268,24 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPublic(final PsiMember psiField) {
|
||||
return psiField.hasModifierProperty(PsiModifier.PUBLIC);
|
||||
@Contract("null -> false")
|
||||
private static boolean isRegularMethod(PsiMethod method) {
|
||||
return method != null && !method.isConstructor();
|
||||
}
|
||||
|
||||
private static String getMethodTypes(PsiMethod method) {
|
||||
/**
|
||||
* Non-public members of superclass/superinterface can't be obtained via reflection, they need to be filtered out.
|
||||
*/
|
||||
@Contract("null, _ -> false")
|
||||
private static boolean isReachable(PsiMember member, PsiClass psiClass) {
|
||||
return member != null && (member.getContainingClass() == psiClass || isPublic(member));
|
||||
}
|
||||
|
||||
private static boolean isPublic(@NotNull PsiMember member) {
|
||||
return member.hasModifierProperty(PsiModifier.PUBLIC);
|
||||
}
|
||||
|
||||
private static String getMethodTypes(@NotNull PsiMethod method) {
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
for (PsiParameter parameter : method.getParameterList().getParameters()) {
|
||||
PsiType type = TypeConversionUtil.erasure(parameter.getType());
|
||||
@@ -273,18 +296,4 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
enum Type {
|
||||
FIELD, DECLARED_FIELD, METHOD, DECLARED_METHOD;
|
||||
|
||||
@Nullable
|
||||
static Type fromString(String s) {
|
||||
if ("getField".equals(s)) return FIELD;
|
||||
if ("getDeclaredField".equals(s)) return DECLARED_FIELD;
|
||||
if ("getMethod".equals(s)) return METHOD;
|
||||
if ("getDeclaredMethod".equals(s)) return DECLARED_METHOD;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,7 @@ public class JavaReflectionReferenceProvider extends PsiReferenceProvider {
|
||||
if (grandParent instanceof PsiMethodCallExpression) {
|
||||
PsiReferenceExpression methodReference = ((PsiMethodCallExpression)grandParent).getMethodExpression();
|
||||
PsiExpression qualifier = methodReference.getQualifierExpression();
|
||||
if (qualifier instanceof PsiClassObjectAccessExpression ||
|
||||
qualifier instanceof PsiMethodCallExpression ||
|
||||
qualifier instanceof PsiReferenceExpression) {
|
||||
if (qualifier != null) {
|
||||
return new PsiReference[]{new JavaLangClassMemberReference(literal, qualifier)};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
class Main {
|
||||
final Parent test = new Test();
|
||||
void foo() {
|
||||
test.getClass().getField("<caret>");
|
||||
}
|
||||
}
|
||||
|
||||
class Test extends Parent {
|
||||
public int num;
|
||||
int num2;
|
||||
}
|
||||
|
||||
class Parent {
|
||||
public int num3;
|
||||
int num4;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
class Main {
|
||||
final Parent test = new Test();
|
||||
void foo() {
|
||||
test.getClass().getField("num2");
|
||||
}
|
||||
}
|
||||
|
||||
class Test extends Parent {
|
||||
public int num;
|
||||
int num2;
|
||||
}
|
||||
|
||||
class Parent {
|
||||
public int num3;
|
||||
int num4;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
class Main {
|
||||
final Class<?> clazz = Test.class;
|
||||
void foo() {
|
||||
clazz.getMethod("<caret>");
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void method(){}
|
||||
void method2(int n){}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
class Main {
|
||||
final Class<?> clazz = Test.class;
|
||||
void foo() {
|
||||
clazz.getMethod("method");
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void method(){}
|
||||
void method2(int n){}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
class Main {
|
||||
void foo() {
|
||||
Class<?> d = d();
|
||||
d = d();
|
||||
int i = 0;
|
||||
(d).getDeclaredMethod("<caret>");
|
||||
}
|
||||
Class<Test> d() {
|
||||
return Test.class;
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public int method() {}
|
||||
int method2(int n) {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
class Main {
|
||||
void foo() {
|
||||
Class<?> d = d();
|
||||
d = d();
|
||||
int i = 0;
|
||||
(d).getDeclaredMethod("method2", int.class);
|
||||
}
|
||||
Class<Test> d() {
|
||||
return Test.class;
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public int method() {}
|
||||
int method2(int n) {}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Main {
|
||||
void foo() {
|
||||
bar().getClass().getField("<caret>");
|
||||
}
|
||||
Test bar() { return null; }
|
||||
}
|
||||
|
||||
class Test {
|
||||
public int num;
|
||||
public int num2;
|
||||
int num3;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Main {
|
||||
void foo() {
|
||||
bar().getClass().getField("num2");
|
||||
}
|
||||
Test bar() { return null; }
|
||||
}
|
||||
|
||||
class Test {
|
||||
public int num;
|
||||
public int num2;
|
||||
int num3;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Main {
|
||||
Test test;
|
||||
void foo() {
|
||||
test.getClass().getField("<caret>");
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public int num;
|
||||
public int num2;
|
||||
int num3;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Main {
|
||||
Test test;
|
||||
void foo() {
|
||||
test.getClass().getField("num2");
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public int num;
|
||||
public int num2;
|
||||
int num3;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class Main {
|
||||
void foo() throws NoSuchMethodException {
|
||||
clazz().getMethod("<caret>");
|
||||
}
|
||||
private Class<? extends Test> clazz() {return Test.class;}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void method(){}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
class Main {
|
||||
void foo() throws NoSuchMethodException {
|
||||
clazz().getMethod("method");
|
||||
}
|
||||
private Class<? extends Test> clazz() {return Test.class;}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void method(){}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
|
||||
}
|
||||
|
||||
public void testField() throws Exception {
|
||||
doTest(1, "num", "num2");
|
||||
doTest(1, "num", "num2", "num3");
|
||||
}
|
||||
|
||||
public void testDeclaredField() throws Exception {
|
||||
@@ -46,7 +46,7 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
|
||||
}
|
||||
|
||||
public void testMethod() throws Exception {
|
||||
doTest(1, "method", "method2");
|
||||
doTest(1, "method", "method2", "method3");
|
||||
}
|
||||
|
||||
public void testForNameDeclaredMethod() throws Exception {
|
||||
@@ -54,11 +54,11 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
|
||||
}
|
||||
|
||||
public void testForNameMethod() throws Exception {
|
||||
doTest(1, "method", "method2");
|
||||
doTest(1, "method", "method2", "method3");
|
||||
}
|
||||
|
||||
public void testForNameField() throws Exception {
|
||||
doTest(1, "num", "num2");
|
||||
doTest(1, "num", "num2", "num3");
|
||||
}
|
||||
|
||||
public void testForNameDeclaredField() throws Exception {
|
||||
@@ -75,7 +75,7 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
|
||||
}
|
||||
|
||||
public void testInheritedMethod() throws Exception {
|
||||
doTest(1, "method", "method2");
|
||||
doTest(1, "method", "method2", "method3");
|
||||
}
|
||||
|
||||
public void testInheritedDeclaredMethod() throws Exception {
|
||||
@@ -83,7 +83,7 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
|
||||
}
|
||||
|
||||
public void testInheritedField() throws Exception {
|
||||
doTest(1, "num", "num2");
|
||||
doTest(1, "num", "num2", "num3");
|
||||
}
|
||||
|
||||
public void testInheritedDeclaredField() throws Exception {
|
||||
@@ -118,6 +118,30 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
|
||||
IdeaTestUtil.withLevel(myFixture.getModule(), LanguageLevel.JDK_1_4, () -> doTest(0, "method"));
|
||||
}
|
||||
|
||||
public void testWildcard() throws Exception {
|
||||
doTest(0, "method");
|
||||
}
|
||||
|
||||
public void testConstantMethod() throws Exception {
|
||||
doTest(0, "method", "method2");
|
||||
}
|
||||
|
||||
public void testDistantDefinition() throws Exception {
|
||||
doTest(1, "method", "method2");
|
||||
}
|
||||
|
||||
public void testVariableGetClassField() throws Exception {
|
||||
doTest(1, "num", "num2", "num3");
|
||||
}
|
||||
|
||||
public void testConstantGetClassField() throws Exception {
|
||||
doTest(1, "num", "num2", "num3");
|
||||
}
|
||||
|
||||
public void testExpressionGetClassField() throws Exception {
|
||||
doTest(1, "num", "num2", "num3");
|
||||
}
|
||||
|
||||
private void doTest(int index, String... expected) {
|
||||
configureByFile(getTestName(false) + ".java");
|
||||
assertStringItems(expected);
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.navigation;
|
||||
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMember;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -35,33 +36,76 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas
|
||||
|
||||
public void testField() {doTest("field", FIELD);}
|
||||
|
||||
public void testField2() {doNegativeTest("field2", FIELD);}
|
||||
public void testField2() {doTest("field2", FIELD);}
|
||||
|
||||
public void testDeclaredField() {doTest("field2", DF);}
|
||||
|
||||
public void testNonexistentField() {doNegativeTest("nonexistent", FIELD);}
|
||||
|
||||
public void testMethod() {doTest("method", METHOD);}
|
||||
|
||||
public void testMethod2() {doNegativeTest("method2", METHOD);}
|
||||
public void testMethod2() {doTest("method2", METHOD);}
|
||||
|
||||
public void testDeclaredMethod() {doTest("method2", DM);}
|
||||
|
||||
public void testNonexistentMethod() {doNegativeTest("nonexistent", METHOD);}
|
||||
|
||||
|
||||
public void testInheritedField() {doTest("field3", FIELD);}
|
||||
|
||||
public void testInheritedField2() {doNegativeTest("field4", FIELD);}
|
||||
public void testInheritedField2() {doTest("field4", FIELD);}
|
||||
|
||||
public void testInheritedDeclaredField() {doNegativeTest("field3", DF);}
|
||||
|
||||
public void testInheritedMethod() {doTest("method3", METHOD);}
|
||||
|
||||
public void testInheritedMethod2() {doNegativeTest("method4", METHOD);}
|
||||
public void testInheritedMethod2() {doTest("method4", METHOD);}
|
||||
|
||||
public void testInheritedDeclaredMethod() {doNegativeTest("method3", DM);}
|
||||
|
||||
public void testConstantClassName() {
|
||||
doCustomTest("method2",
|
||||
"class Main {" +
|
||||
" static final String NAME = \"Test\";" +
|
||||
" void foo() throws ReflectiveOperationException {" +
|
||||
" Class.forName(NAME).getMethod(\"<caret>method2\", int.class);" +
|
||||
" }" +
|
||||
"}");
|
||||
}
|
||||
|
||||
public void testVariableClassName() {
|
||||
doCustomTest("method",
|
||||
"class Main {" +
|
||||
" void foo() throws ReflectiveOperationException {" +
|
||||
" String name;" +
|
||||
" name = \"Te\" + \"st\";" +
|
||||
" Class.forName(name).getMethod(\"<caret>method\");" +
|
||||
" }" +
|
||||
"}");
|
||||
}
|
||||
|
||||
public void testExpressionClassName() {
|
||||
doCustomTest("method3",
|
||||
"class Main {" +
|
||||
" void foo() throws ReflectiveOperationException {" +
|
||||
" Class.forName(\"Pa\" + \"rent\").getMethod(\"<caret>method3\");" +
|
||||
" }" +
|
||||
"}");
|
||||
}
|
||||
|
||||
|
||||
private void doTest(String name,
|
||||
@MagicConstant(stringValues = {FIELD, METHOD, DF, DM}) String type) {
|
||||
PsiReference reference = getReference(name, type);
|
||||
doTestImpl(name, getMainClassText(name, type));
|
||||
}
|
||||
|
||||
private void doCustomTest(String name,
|
||||
@NotNull @NonNls @Language("JAVA") String mainClassText) {
|
||||
doTestImpl(name, mainClassText);
|
||||
}
|
||||
|
||||
private void doTestImpl(String name, String mainClassText) {
|
||||
PsiReference reference = getReference(mainClassText);
|
||||
assertEquals("Reference text", name, reference.getCanonicalText());
|
||||
PsiElement resolved = reference.resolve();
|
||||
assertNotNull("Reference is not resolved: " + reference.getCanonicalText(), resolved);
|
||||
@@ -72,15 +116,14 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas
|
||||
|
||||
private void doNegativeTest(String name,
|
||||
@MagicConstant(stringValues = {FIELD, METHOD, DF, DM}) String type) {
|
||||
PsiReference reference = getReference(name, type);
|
||||
PsiReference reference = getReference(getMainClassText(name, type));
|
||||
assertEquals("Reference text", name, reference.getCanonicalText());
|
||||
PsiElement resolved = reference.resolve();
|
||||
assertNull("Reference shouldn't resolve: " + reference.getCanonicalText(), resolved);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiReference getReference(String name,
|
||||
@MagicConstant(stringValues = {FIELD, METHOD, DF, DM}) String type) {
|
||||
private PsiReference getReference(String mainClassText) {
|
||||
myFixture.addClass("class Parent {\n" +
|
||||
" public int field3;\n" +
|
||||
" int field4;\n" +
|
||||
@@ -93,12 +136,7 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas
|
||||
" public void method() {}\n" +
|
||||
" void method2(int n) {}\n" +
|
||||
"}");
|
||||
myFixture.configureByText("Main.java",
|
||||
"class Main {\n" +
|
||||
" void foo() throws ReflectiveOperationException {\n" +
|
||||
" Test.class.get" + type + "(\"<caret>" + name + "\");\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
myFixture.configureByText("Main.java", mainClassText);
|
||||
|
||||
int offset = myFixture.getCaretOffset();
|
||||
PsiReference reference = myFixture.getFile().findReferenceAt(offset);
|
||||
@@ -107,6 +145,12 @@ public class JavaReflectionNavigationTest extends LightCodeInsightFixtureTestCas
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getTestDataPath() {return PathManagerEx.getTestDataPath() + "/codeInsight/navigation/reflection";}
|
||||
private static String getMainClassText(String name,
|
||||
@MagicConstant(stringValues = {FIELD, METHOD, DF, DM}) String type) {
|
||||
return "class Main {\n" +
|
||||
" void foo() throws ReflectiveOperationException {\n" +
|
||||
" Test.class.get" + type + "(\"<caret>" + name + "\");\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user