Java: Provide completion for arguments of Class.getAnnotation() and Class.getConstructor(), tests added (IDEA-167250)

This commit is contained in:
Pavel Dolgov
2017-02-10 13:05:28 +03:00
parent ece87efcca
commit 24e71dc952
21 changed files with 567 additions and 147 deletions
@@ -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();
}
}
@@ -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);
}
}
}
@@ -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>);
}
}
@@ -0,0 +1,7 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Class<Annotation> aType = Baz.class;
Test.class.getAnnotation(Bar.class);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Test.class.getAnnotationsByType(<caret>);
}
}
@@ -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>);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Construct.class.getConstructor(int.class, String.class);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Test.class.getDeclaredAnnotation(<caret>);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Test.class.getDeclaredAnnotation(Bar.class);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Test.class.getDeclaredAnnotationsByType(<caret>);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Test.class.getDeclaredAnnotationsByType(Foo.class);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Construct.class.getDeclaredConstructor(<caret>);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Construct.class.getDeclaredConstructor();
}
}
@@ -0,0 +1,7 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Class<Annotation> aType = Baz.class;
Test.class.getAnnotation(<caret>);
}
}
@@ -0,0 +1,7 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Class<Annotation> aType = Baz.class;
Test.class.getAnnotation(Foo.class);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Test.class.getDeclaredAnnotation(<caret>);
}
}
@@ -0,0 +1,6 @@
import foo.bar.*;
class Main {
void foo() throws ReflectiveOperationException {
Test.class.getDeclaredAnnotation(Foo.class);
}
}
@@ -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() {}
}""")
}
}
+2
View File
@@ -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"/>