mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
Java: Provide completion for arguments of Class.getAnnotation() and Class.getConstructor(), tests added (IDEA-167250)
This commit is contained in:
+8
-147
@@ -20,24 +20,18 @@ import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.openapi.project.Project;
|
||||
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.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
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;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*;
|
||||
|
||||
/**
|
||||
* @author Konstantin Bulenkov
|
||||
*/
|
||||
@@ -47,7 +41,6 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
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(@NotNull PsiLiteralExpression literal, @NotNull PsiExpression context) {
|
||||
@@ -103,107 +96,7 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
|
||||
@Nullable
|
||||
private PsiClass getPsiClass() {
|
||||
return getPsiClass(myContext);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass getPsiClass(PsiExpression context) {
|
||||
context = ParenthesesUtils.stripParentheses(context);
|
||||
if (context instanceof PsiClassObjectAccessExpression) { // special case for JDK 1.4
|
||||
PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand();
|
||||
return PsiTypesUtil.getPsiClass(operand.getType());
|
||||
}
|
||||
|
||||
if (context instanceof PsiMethodCallExpression) {
|
||||
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) {
|
||||
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)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) {
|
||||
final PsiElement resolved = ((PsiReferenceExpression)context).resolve();
|
||||
if (resolved instanceof PsiVariable) {
|
||||
final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)context, (PsiVariable)resolved);
|
||||
if (definition != null) {
|
||||
return ourGuard.doPreventingRecursion(resolved, false, () -> getPsiClass(definition));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return DeclarationSearchUtils.findDefinition(referenceExpression, variable);
|
||||
}
|
||||
|
||||
private static boolean isJavaLangClass(PsiClass aClass) {
|
||||
return aClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(aClass.getQualifiedName());
|
||||
}
|
||||
|
||||
private static boolean isJavaLangObject(PsiClass aClass) {
|
||||
return aClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName());
|
||||
return getReflectiveClass(myContext);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -257,43 +150,11 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
|
||||
final int start = newElement.getTextRange().getEndOffset();
|
||||
final PsiElement params = newElement.getParent().getParent();
|
||||
final int end = params.getTextRange().getEndOffset() - 1;
|
||||
final String types = getMethodTypes((PsiMethod)object);
|
||||
String types = getParameterTypesText((PsiMethod)object);
|
||||
if (!types.isEmpty()) types = ", " + types;
|
||||
context.getDocument().replaceString(start, end, types);
|
||||
context.commitDocument();
|
||||
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);
|
||||
}
|
||||
shortenArgumentsClassReferences(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
private static boolean isRegularMethod(PsiMethod method) {
|
||||
return method != null && !method.isConstructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
if (type instanceof PsiEllipsisType) {
|
||||
type = new PsiArrayType(((PsiEllipsisType)type).getComponentType());
|
||||
}
|
||||
buf.append(", ").append(type.getPresentableText()).append(".class");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.AnnotationUtil;
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.patterns.ElementPattern;
|
||||
import com.intellij.patterns.PatternCondition;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
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 JavaReflectionCompletionContributor extends CompletionContributor {
|
||||
|
||||
private static final ElementPattern<? extends PsiElement> CONSTRUCTOR_ARGUMENTS = psiElement(PsiExpressionList.class)
|
||||
.withParent(psiExpression().methodCall(
|
||||
psiMethod()
|
||||
.withName("getConstructor", "getDeclaredConstructor")
|
||||
.definedInClass(CommonClassNames.JAVA_LANG_CLASS)));
|
||||
|
||||
private static final ElementPattern<? extends PsiElement> ANNOTATION_ARGUMENTS = psiElement(PsiExpressionList.class)
|
||||
.withParent(psiExpression().methodCall(
|
||||
psiMethod()
|
||||
.withName("getAnnotation", "getDeclaredAnnotation", "getAnnotationsByType", "getDeclaredAnnotationsByType")
|
||||
.with(new MethodDefinedInInterfacePatternCondition("java.lang.reflect.AnnotatedElement"))));
|
||||
|
||||
private static final ElementPattern<PsiElement> BEGINNING_OF_CONSTRUCTOR_ARGUMENTS = beginningOfArguments(CONSTRUCTOR_ARGUMENTS);
|
||||
|
||||
private static final ElementPattern<PsiElement> BEGINNING_OF_ANNOTATION_ARGUMENTS = beginningOfArguments(ANNOTATION_ARGUMENTS);
|
||||
|
||||
private static ElementPattern<PsiElement> beginningOfArguments(ElementPattern<? extends PsiElement> argumentsPattern) {
|
||||
return psiElement().afterLeaf("(").withParent(
|
||||
or(psiExpression().withParent(argumentsPattern),
|
||||
psiElement().withParent(PsiTypeElement.class) // special case for getConstructor(int.class) because 'int' is a keyword
|
||||
.withSuperParent(2, PsiClassObjectAccessExpression.class)
|
||||
.withSuperParent(3, argumentsPattern)));
|
||||
}
|
||||
|
||||
@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 (BEGINNING_OF_ANNOTATION_ARGUMENTS.accepts(position)) {
|
||||
PsiClass psiClass = getQualifierClass(position);
|
||||
if (psiClass != null) {
|
||||
addAnnotationClasses(psiClass, result);
|
||||
}
|
||||
//TODO handle annotations on fields and methods
|
||||
}
|
||||
if (BEGINNING_OF_CONSTRUCTOR_ARGUMENTS.accepts(position)) {
|
||||
PsiClass psiClass = getQualifierClass(position);
|
||||
if (psiClass != null) {
|
||||
addConstructorParameterTypes(psiClass, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass getQualifierClass(@Nullable PsiElement position) {
|
||||
PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(position, PsiMethodCallExpression.class);
|
||||
return methodCall != null ? getReflectiveClass(methodCall.getMethodExpression().getQualifierExpression()) : null;
|
||||
}
|
||||
|
||||
private static void addAnnotationClasses(@NotNull PsiModifierListOwner annotationsOwner, @NotNull CompletionResultSet result) {
|
||||
PsiAnnotation[] annotations = AnnotationUtil.getAllAnnotations(annotationsOwner, true, null);
|
||||
for (PsiAnnotation annotation : annotations) {
|
||||
PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
|
||||
if (referenceElement != null) {
|
||||
PsiElement resolved = referenceElement.resolve();
|
||||
if (resolved instanceof PsiClass) {
|
||||
PsiClass annotationClass = (PsiClass)resolved;
|
||||
String className = annotationClass.getName();
|
||||
if (className != null) {
|
||||
LookupElement lookupElement = LookupElementBuilder.createWithIcon(annotationClass)
|
||||
.withPresentableText(className + ".class")
|
||||
.withInsertHandler(JavaReflectionCompletionContributor::handleAnnotationClassInsertion);
|
||||
result.addElement(lookupElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addConstructorParameterTypes(@NotNull PsiClass psiClass, @NotNull CompletionResultSet result) {
|
||||
PsiMethod[] constructors = psiClass.getConstructors();
|
||||
if (constructors.length != 0) {
|
||||
for (PsiMethod constructor : constructors) {
|
||||
String parameterTypesText = Arrays.stream(constructor.getParameterList().getParameters())
|
||||
.map(p -> p.getType().getCanonicalText())
|
||||
.collect(Collectors.joining(",", constructor.getName() + "(", ")"));
|
||||
|
||||
LookupElement lookupElement = LookupElementBuilder.createWithIcon(constructor)
|
||||
.withPresentableText(parameterTypesText)
|
||||
.withInsertHandler(JavaReflectionCompletionContributor::handleConstructorSignatureInsertion);
|
||||
result.addElement(lookupElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleAnnotationClassInsertion(@NotNull InsertionContext context, @NotNull LookupElement item) {
|
||||
Object object = item.getObject();
|
||||
if (object instanceof PsiClass) {
|
||||
String className = ((PsiClass)object).getName();
|
||||
if (className != null) {
|
||||
handleParametersInsertion(context, className + ".class");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleConstructorSignatureInsertion(@NotNull InsertionContext context, @NotNull LookupElement item) {
|
||||
Object object = item.getObject();
|
||||
if (object instanceof PsiMethod) {
|
||||
handleParametersInsertion(context, getParameterTypesText((PsiMethod)object));
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleParametersInsertion(@NotNull InsertionContext context, @NotNull String text) {
|
||||
PsiElement newElement = PsiUtilCore.getElementAtOffset(context.getFile(), context.getStartOffset());
|
||||
PsiExpressionList parameterList = PsiTreeUtil.getParentOfType(newElement, PsiExpressionList.class);
|
||||
if (parameterList != null) {
|
||||
final TextRange range = parameterList.getTextRange();
|
||||
context.getDocument().replaceString(range.getStartOffset(), range.getEndOffset(), "(" + text + ")");
|
||||
context.commitDocument();
|
||||
shortenArgumentsClassReferences(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MethodDefinedInInterfacePatternCondition extends PatternCondition<PsiMethod> {
|
||||
private final String myInterfaceName;
|
||||
|
||||
public MethodDefinedInInterfacePatternCondition(@NotNull String interfaceName) {
|
||||
super("definedInInterface");
|
||||
myInterfaceName = interfaceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(@NotNull PsiMethod method, ProcessingContext context) {
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
return InheritanceUtil.isInheritor(containingClass, false, myInterfaceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.InsertionContext;
|
||||
import com.intellij.openapi.project.Project;
|
||||
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.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
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;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
class JavaReflectionReferenceUtil {
|
||||
private static final RecursionGuard ourGuard = RecursionManager.createGuard("JavaLangClassMemberReference");
|
||||
|
||||
@Nullable
|
||||
static PsiClass getReflectiveClass(PsiExpression context) {
|
||||
context = ParenthesesUtils.stripParentheses(context);
|
||||
if (context instanceof PsiClassObjectAccessExpression) { // special case for JDK 1.4
|
||||
PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand();
|
||||
return PsiTypesUtil.getPsiClass(operand.getType());
|
||||
}
|
||||
|
||||
if (context instanceof PsiMethodCallExpression) {
|
||||
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) {
|
||||
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)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) {
|
||||
final PsiElement resolved = ((PsiReferenceExpression)context).resolve();
|
||||
if (resolved instanceof PsiVariable) {
|
||||
final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)context, (PsiVariable)resolved);
|
||||
if (definition != null) {
|
||||
return ourGuard.doPreventingRecursion(resolved, false, () -> getReflectiveClass(definition));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return DeclarationSearchUtils.findDefinition(referenceExpression, variable);
|
||||
}
|
||||
|
||||
static boolean isJavaLangClass(PsiClass aClass) {
|
||||
return aClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(aClass.getQualifiedName());
|
||||
}
|
||||
|
||||
static boolean isJavaLangObject(PsiClass aClass) {
|
||||
return aClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName());
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
static boolean isRegularMethod(PsiMethod method) {
|
||||
return method != null && !method.isConstructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-public members of superclass/superinterface can't be obtained via reflection, they need to be filtered out.
|
||||
*/
|
||||
@Contract("null, _ -> false")
|
||||
static boolean isReachable(PsiMember member, PsiClass psiClass) {
|
||||
return member != null && (member.getContainingClass() == psiClass || isPublic(member));
|
||||
}
|
||||
|
||||
static boolean isPublic(@NotNull PsiMember member) {
|
||||
return member.hasModifierProperty(PsiModifier.PUBLIC);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static String getParameterTypesText(@NotNull PsiMethod method) {
|
||||
return Arrays.stream(method.getParameterList().getParameters())
|
||||
.map(parameter -> TypeConversionUtil.erasure(parameter.getType()))
|
||||
.map(type -> (type instanceof PsiEllipsisType) ? new PsiArrayType(((PsiEllipsisType)type).getComponentType()) : type)
|
||||
.map(type -> type.getPresentableText() + ".class")
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(<caret>);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(Bar.class);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getAnnotationsByType(<caret>);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getAnnotationsByType(Bar.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Construct.class.getConstructor(<caret>);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Construct.class.getConstructor(int.class, String.class);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getDeclaredAnnotation(<caret>);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getDeclaredAnnotation(Bar.class);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getDeclaredAnnotationsByType(<caret>);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getDeclaredAnnotationsByType(Foo.class);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Construct.class.getDeclaredConstructor(<caret>);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Construct.class.getDeclaredConstructor();
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(<caret>);
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Class<Annotation> aType = Baz.class;
|
||||
Test.class.getAnnotation(Foo.class);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getDeclaredAnnotation(<caret>);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import foo.bar.*;
|
||||
class Main {
|
||||
void foo() throws ReflectiveOperationException {
|
||||
Test.class.getDeclaredAnnotation(Foo.class);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* 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.codeInsight.completion
|
||||
|
||||
import com.intellij.JavaTestUtil
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
class JavaReflectionParametersCompletionTest : LightFixtureCompletionTestCase() {
|
||||
override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/completion/reflectionParameters/"
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase.JAVA_8
|
||||
|
||||
fun testAnnotation() = doTest(0, "Bar.class", "Foo.class", "aType")
|
||||
|
||||
fun testInheritedAnnotation() = doTest(1, "Bar.class", "Foo.class", "aType")
|
||||
|
||||
fun testDeclaredAnnotation() = doTest(0, "Bar.class", "Foo.class")
|
||||
|
||||
fun testInheritedDeclaredAnnotation() = doTest(1, "Bar.class", "Foo.class")
|
||||
|
||||
fun testAnnotationsByType() = doTest(0, "Bar.class", "Foo.class")
|
||||
|
||||
fun testDeclaredAnnotationsByType() = doTest(1, "Bar.class", "Foo.class")
|
||||
|
||||
fun testConstructor() {
|
||||
addConstructors()
|
||||
doTest(2, "Construct()", "Construct(int)", "Construct(int,java.lang.String)", "Construct(java.lang.String)")
|
||||
}
|
||||
|
||||
fun testDeclaredConstructor() {
|
||||
addConstructors()
|
||||
doTest(0, "Construct()", "Construct(int)", "Construct(int,java.lang.String)", "Construct(java.lang.String)")
|
||||
}
|
||||
|
||||
private fun doTest(index: Int, vararg expected: String) {
|
||||
addClasses()
|
||||
configureByFile(getTestName(false) + ".java")
|
||||
|
||||
val lookupItems = lookup.items
|
||||
val texts = lookupItems.subList(0, Math.min(lookupItems.size, expected.size)).map {
|
||||
val presentation = LookupElementPresentation()
|
||||
it?.renderElement(presentation)
|
||||
presentation.itemText ?: ""
|
||||
}
|
||||
assertOrderedEquals(texts, *expected)
|
||||
selectItem(lookupItems[index])
|
||||
myFixture.checkResultByFile(getTestName(false) + "_after.java")
|
||||
}
|
||||
|
||||
private fun addClasses() {
|
||||
myFixture.addClass("package foo.bar; public @interface Foo {}")
|
||||
myFixture.addClass("package foo.bar; public @interface Bar {}")
|
||||
myFixture.addClass("package foo.bar; public @interface Baz {}")
|
||||
myFixture.addClass("package foo.bar; @Foo class Parent {}")
|
||||
myFixture.addClass("package foo.bar; @Bar class Test extends Parent {}")
|
||||
}
|
||||
|
||||
private fun addConstructors() {
|
||||
myFixture.addClass("""package foo.bar;
|
||||
public class Construct {
|
||||
public Construct(int n, String s) {}
|
||||
Construct(int n) {}
|
||||
Construct(String s) {}
|
||||
public Construct() {}
|
||||
}""")
|
||||
}
|
||||
}
|
||||
@@ -425,6 +425,8 @@
|
||||
order="last, before javaSmart"/>
|
||||
<completion.contributor language="JAVA" implementationClass="com.intellij.codeInsight.completion.JavaSmartCompletionContributor" id="javaSmart"
|
||||
order="last, before default"/>
|
||||
<completion.contributor language="JAVA" implementationClass="com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionCompletionContributor" id="javaReflection"
|
||||
order="last, before javaLegacy"/>
|
||||
|
||||
<lookup.charFilter implementation="com.intellij.codeInsight.completion.JavaCharFilter" id="java"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user