mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java] Support magic constants in enum constructor call (inspection+completion)
Fixes IDEA-274956 MagicConstants don't work in enum constructors Also support non-public accessible constants in valuesFromClass GitOrigin-RevId: 32daea7b636573cb599c21d634b26f61a91af69d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ce48b06ff3
commit
e0e69c4944
+23
-8
@@ -58,7 +58,7 @@ public final class MagicCompletionContributor extends CompletionContributor impl
|
||||
public static MagicConstantUtils.AllowedValues getAllowedValues(@NotNull PsiElement pos) {
|
||||
MagicConstantUtils.AllowedValues allowedValues = null;
|
||||
for (Pair<PsiModifierListOwner, PsiType> pair : getMembersWithAllowedValues(pos)) {
|
||||
MagicConstantUtils.AllowedValues values = MagicConstantUtils.getAllowedValues(pair.first, pair.second);
|
||||
MagicConstantUtils.AllowedValues values = MagicConstantUtils.getAllowedValues(pair.first, pair.second, pos);
|
||||
if (values == null) continue;
|
||||
if (allowedValues == null) {
|
||||
allowedValues = values;
|
||||
@@ -89,15 +89,10 @@ public final class MagicCompletionContributor extends CompletionContributor impl
|
||||
Set<Pair<PsiModifierListOwner, PsiType>> result = new HashSet<>();
|
||||
if (IN_METHOD_CALL_ARGUMENT.accepts(pos)) {
|
||||
PsiCall call = PsiTreeUtil.getParentOfType(pos, PsiCall.class);
|
||||
if (!(call instanceof PsiExpression)) return Collections.emptyList();
|
||||
PsiType type = ((PsiExpression)call).getType();
|
||||
if (call == null) return Collections.emptyList();
|
||||
|
||||
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(call.getProject()).getResolveHelper();
|
||||
JavaResolveResult[] methods = call instanceof PsiMethodCallExpression
|
||||
? ((PsiMethodCallExpression)call).getMethodExpression().multiResolve(true)
|
||||
: call instanceof PsiNewExpression && type instanceof PsiClassType
|
||||
? resolveHelper.multiResolveConstructor((PsiClassType)type, call.getArgumentList(), call)
|
||||
: JavaResolveResult.EMPTY_ARRAY;
|
||||
JavaResolveResult[] methods = getMethodCandidates(call, resolveHelper);
|
||||
for (JavaResolveResult resolveResult : methods) {
|
||||
PsiElement element = resolveResult.getElement();
|
||||
if (!(element instanceof PsiMethod)) return Collections.emptyList();
|
||||
@@ -180,6 +175,26 @@ public final class MagicCompletionContributor extends CompletionContributor impl
|
||||
return new ArrayList<>(result);
|
||||
}
|
||||
|
||||
private static @NotNull JavaResolveResult @NotNull [] getMethodCandidates(PsiCall call, PsiResolveHelper resolveHelper) {
|
||||
if (call instanceof PsiMethodCallExpression) {
|
||||
return ((PsiMethodCallExpression)call).getMethodExpression().multiResolve(true);
|
||||
}
|
||||
if (call instanceof PsiNewExpression) {
|
||||
PsiType type = ((PsiExpression)call).getType();
|
||||
PsiExpressionList argumentList = call.getArgumentList();
|
||||
if (type instanceof PsiClassType && argumentList != null) {
|
||||
return resolveHelper.multiResolveConstructor((PsiClassType)type, argumentList, call);
|
||||
}
|
||||
}
|
||||
if (call instanceof PsiEnumConstant) {
|
||||
JavaResolveResult result = call.resolveMethodGenerics();
|
||||
if (result != JavaResolveResult.EMPTY) {
|
||||
return new JavaResolveResult[]{result};
|
||||
}
|
||||
}
|
||||
return JavaResolveResult.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
private static void addCompletionVariants(@NotNull final CompletionParameters parameters,
|
||||
@NotNull final CompletionResultSet result,
|
||||
PsiElement pos,
|
||||
|
||||
+9
-4
@@ -80,6 +80,11 @@ public final class MagicConstantInspection extends AbstractBaseJavaLocalInspecti
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnumConstant(PsiEnumConstant enumConstant) {
|
||||
checkCall(enumConstant, holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCallExpression(PsiCallExpression callExpression) {
|
||||
checkCall(callExpression, holder);
|
||||
@@ -203,7 +208,7 @@ public final class MagicConstantInspection extends AbstractBaseJavaLocalInspecti
|
||||
@NotNull PsiModifierListOwner owner,
|
||||
@Nullable PsiType type,
|
||||
@NotNull ProblemsHolder holder) {
|
||||
AllowedValues allowed = MagicConstantUtils.getAllowedValues(owner, type);
|
||||
AllowedValues allowed = MagicConstantUtils.getAllowedValues(owner, type, expression);
|
||||
checkExpression(expression, holder, allowed);
|
||||
}
|
||||
|
||||
@@ -218,7 +223,7 @@ public final class MagicConstantInspection extends AbstractBaseJavaLocalInspecti
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkCall(@NotNull PsiCallExpression methodCall, @NotNull ProblemsHolder holder) {
|
||||
private static void checkCall(@NotNull PsiCall methodCall, @NotNull ProblemsHolder holder) {
|
||||
PsiExpressionList argumentList = methodCall.getArgumentList();
|
||||
if (argumentList == null) return;
|
||||
PsiMethod method = methodCall.resolveMethod();
|
||||
@@ -233,7 +238,7 @@ public final class MagicConstantInspection extends AbstractBaseJavaLocalInspecti
|
||||
type = ((PsiEllipsisType)type).getComponentType();
|
||||
stopArg = arguments.length - 1;
|
||||
}
|
||||
AllowedValues values = MagicConstantUtils.getAllowedValues(parameter, type);
|
||||
AllowedValues values = MagicConstantUtils.getAllowedValues(parameter, type, methodCall);
|
||||
if (values == null) continue;
|
||||
if (i >= arguments.length) break;
|
||||
for (int j = i; j <= stopArg; j++) {
|
||||
@@ -432,7 +437,7 @@ public final class MagicConstantInspection extends AbstractBaseJavaLocalInspecti
|
||||
}
|
||||
|
||||
if (allowedForRef == null && resolved instanceof PsiModifierListOwner) {
|
||||
allowedForRef = MagicConstantUtils.getAllowedValues((PsiModifierListOwner)resolved, getType((PsiModifierListOwner)resolved));
|
||||
allowedForRef = MagicConstantUtils.getAllowedValues((PsiModifierListOwner)resolved, getType((PsiModifierListOwner)resolved), expression);
|
||||
}
|
||||
if (allowedForRef != null && allowedForRef.isSubsetOf(allowedValues, manager)) {
|
||||
return true;
|
||||
|
||||
+41
-10
@@ -24,7 +24,8 @@ import java.util.*;
|
||||
public final class MagicConstantUtils {
|
||||
private static AllowedValues getAllowedValuesFromMagic(@NotNull PsiType type,
|
||||
@NotNull PsiAnnotation magic,
|
||||
@NotNull PsiManager manager) {
|
||||
@NotNull PsiManager manager,
|
||||
@Nullable PsiElement context) {
|
||||
PsiAnnotationMemberValue[] allowedValues = PsiAnnotationMemberValue.EMPTY_ARRAY;
|
||||
boolean values = false;
|
||||
boolean flags = false;
|
||||
@@ -62,12 +63,12 @@ public final class MagicConstantUtils {
|
||||
return null; //other types not supported
|
||||
}
|
||||
|
||||
PsiAnnotationMemberValue[] valuesFromClass = readFromClass("valuesFromClass", magic, type, manager);
|
||||
PsiAnnotationMemberValue[] valuesFromClass = readFromClass("valuesFromClass", magic, type, manager, context);
|
||||
if (valuesFromClass != null) {
|
||||
allowedValues = ArrayUtil.mergeArrays(allowedValues, valuesFromClass, PsiAnnotationMemberValue.ARRAY_FACTORY);
|
||||
values = true;
|
||||
}
|
||||
PsiAnnotationMemberValue[] flagsFromClass = readFromClass("flagsFromClass", magic, type, manager);
|
||||
PsiAnnotationMemberValue[] flagsFromClass = readFromClass("flagsFromClass", magic, type, manager, context);
|
||||
if (flagsFromClass != null) {
|
||||
allowedValues = ArrayUtil.mergeArrays(allowedValues, flagsFromClass, PsiAnnotationMemberValue.ARRAY_FACTORY);
|
||||
flags = true;
|
||||
@@ -85,19 +86,29 @@ public final class MagicConstantUtils {
|
||||
private static PsiAnnotationMemberValue[] readFromClass(@NonNls @NotNull String attributeName,
|
||||
@NotNull PsiAnnotation magic,
|
||||
@NotNull PsiType type,
|
||||
@NotNull PsiManager manager) {
|
||||
@NotNull PsiManager manager,
|
||||
@Nullable PsiElement context) {
|
||||
PsiAnnotationMemberValue fromClassAttr = magic.findAttributeValue(attributeName);
|
||||
PsiType fromClassType = fromClassAttr instanceof PsiClassObjectAccessExpression ? ((PsiClassObjectAccessExpression)fromClassAttr).getOperand().getType() : null;
|
||||
PsiType fromClassType = fromClassAttr instanceof PsiClassObjectAccessExpression
|
||||
? ((PsiClassObjectAccessExpression)fromClassAttr).getOperand().getType()
|
||||
: null;
|
||||
PsiClass fromClass = fromClassType instanceof PsiClassType ? ((PsiClassType)fromClassType).resolve() : null;
|
||||
if (fromClass == null) return null;
|
||||
String fqn = fromClass.getQualifiedName();
|
||||
if (fqn == null) return null;
|
||||
List<PsiAnnotationMemberValue> constants = new ArrayList<>();
|
||||
for (PsiField field : fromClass.getFields()) {
|
||||
if (!field.hasModifierProperty(PsiModifier.PUBLIC) || !field.hasModifierProperty(PsiModifier.STATIC) || !field.hasModifierProperty(PsiModifier.FINAL)) continue;
|
||||
if (!field.hasModifierProperty(PsiModifier.STATIC) || !field.hasModifierProperty(PsiModifier.FINAL)) continue;
|
||||
if (!field.hasModifierProperty(PsiModifier.PUBLIC)) {
|
||||
if (context == null ||
|
||||
!JavaPsiFacade.getInstance(manager.getProject()).getResolveHelper().isAccessible(field, context, null)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
PsiType fieldType = field.getType();
|
||||
if (!Comparing.equal(fieldType, type)) continue;
|
||||
PsiAssignmentExpression e = (PsiAssignmentExpression)JavaPsiFacade.getElementFactory(manager.getProject()).createExpressionFromText("x="+fqn + "." + field.getName(), field);
|
||||
PsiAssignmentExpression e = (PsiAssignmentExpression)JavaPsiFacade.getElementFactory(manager.getProject())
|
||||
.createExpressionFromText("x=" + fqn + "." + field.getName(), field);
|
||||
PsiReferenceExpression refToField = (PsiReferenceExpression)e.getRExpression();
|
||||
constants.add(refToField);
|
||||
}
|
||||
@@ -106,17 +117,37 @@ public final class MagicConstantUtils {
|
||||
return constants.toArray(PsiAnnotationMemberValue.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated used {@link #getAllowedValues(PsiModifierListOwner, PsiType, PsiElement)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public static AllowedValues getAllowedValues(@NotNull PsiModifierListOwner element, @Nullable PsiType type) {
|
||||
return getAllowedValues(element, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param element element with possible MagicConstant annotation
|
||||
* @param type element type
|
||||
* @param context context where annotation is applied (to check the accessibility of magic constant)
|
||||
* @return possible allowed values to be used instead of constant literal; null if no MagicConstant annotation found
|
||||
*/
|
||||
@Nullable
|
||||
static AllowedValues getAllowedValues(@NotNull PsiModifierListOwner element, @Nullable PsiType type, @Nullable Set<? super PsiClass> visited) {
|
||||
public static AllowedValues getAllowedValues(@NotNull PsiModifierListOwner element,
|
||||
@Nullable PsiType type,
|
||||
@Nullable PsiElement context) {
|
||||
return getAllowedValues(element, type, context, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static AllowedValues getAllowedValues(@NotNull PsiModifierListOwner element,
|
||||
@Nullable PsiType type,
|
||||
@Nullable PsiElement context,
|
||||
@Nullable Set<? super PsiClass> visited) {
|
||||
PsiManager manager = element.getManager();
|
||||
for (PsiAnnotation annotation : getAllAnnotations(element)) {
|
||||
if (type != null && MagicConstant.class.getName().equals(annotation.getQualifiedName())) {
|
||||
AllowedValues values = getAllowedValuesFromMagic(type, annotation, manager);
|
||||
AllowedValues values = getAllowedValuesFromMagic(type, annotation, manager, context);
|
||||
if (values != null) return values;
|
||||
}
|
||||
|
||||
@@ -129,7 +160,7 @@ public final class MagicConstantUtils {
|
||||
if (!visited.add(aClass)) {
|
||||
continue;
|
||||
}
|
||||
AllowedValues values = getAllowedValues(aClass, type, visited);
|
||||
AllowedValues values = getAllowedValues(aClass, type, context, visited);
|
||||
if (values != null) {
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
|
||||
enum EnumConstructor {
|
||||
FOO(<warning descr="Should be one of: MagicConstantIds.ONE">1</warning>),
|
||||
;
|
||||
|
||||
private final int magicConstant;
|
||||
|
||||
EnumConstructor(@MagicConstant(valuesFromClass = MagicConstantIds.class) int magicConstant) {
|
||||
this.magicConstant = magicConstant;
|
||||
}
|
||||
}
|
||||
class MagicConstantIds {
|
||||
static final int ONE = 1;
|
||||
private static final int TWO = 2;
|
||||
}
|
||||
+25
@@ -81,6 +81,31 @@ interface Foo {
|
||||
myFixture.assertPreferredCompletionItems 0, 'BAR', 'FOO'
|
||||
}
|
||||
|
||||
@NeedsIndex.ForStandardLibrary
|
||||
void "test completion in enum constructor"() {
|
||||
addMagicConstant()
|
||||
myFixture.configureByText "a.java", """
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
|
||||
enum MagicConstantTest {
|
||||
FOO(<caret>),
|
||||
;
|
||||
|
||||
private final int magicConstant;
|
||||
|
||||
MagicConstantTest(@MagicConstant(valuesFromClass = MagicConstantIds.class) int magicConstant) {
|
||||
this.magicConstant = magicConstant;
|
||||
}
|
||||
}
|
||||
class MagicConstantIds {
|
||||
static final int ONE = 1;
|
||||
static final int TWO = 2;
|
||||
}
|
||||
"""
|
||||
myFixture.completeBasic()
|
||||
myFixture.assertPreferredCompletionItems 0, 'ONE', 'TWO'
|
||||
}
|
||||
|
||||
void "test magic constant in equality before another equality"() {
|
||||
addMagicConstant()
|
||||
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ public class MagicConstantInspectionTest extends LightJavaCodeInsightFixtureTest
|
||||
public void testWithLibrary() { doTest(); }
|
||||
public void testSpecialCases() { doTest(); }
|
||||
public void testVarargMethodCall() { doTest(); }
|
||||
public void testEnumConstructor() { doTest(); }
|
||||
|
||||
private void doTest() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
|
||||
+4
-4
@@ -100,22 +100,22 @@ public class CreateMissingSwitchBranchesAction extends PsiElementBaseIntentionAc
|
||||
}
|
||||
if (expression instanceof PsiReferenceExpression) {
|
||||
PsiModifierListOwner target = ObjectUtils.tryCast(((PsiReferenceExpression)expression).resolve(), PsiModifierListOwner.class);
|
||||
List<Value> values = getValues(target, type);
|
||||
List<Value> values = getValues(target, type, expression);
|
||||
if (values != null) return values;
|
||||
}
|
||||
else if (expression instanceof PsiMethodCallExpression) {
|
||||
PsiModifierListOwner target = ObjectUtils.tryCast(((PsiMethodCallExpression)expression).resolveMethod(), PsiModifierListOwner.class);
|
||||
List<Value> values = getValues(target, type);
|
||||
List<Value> values = getValues(target, type, expression);
|
||||
if (values != null) return values;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<Value> getValues(PsiModifierListOwner target, PsiType type) {
|
||||
private static List<Value> getValues(PsiModifierListOwner target, PsiType type, PsiElement context) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
MagicConstantUtils.AllowedValues values = MagicConstantUtils.getAllowedValues(target, type);
|
||||
MagicConstantUtils.AllowedValues values = MagicConstantUtils.getAllowedValues(target, type, context);
|
||||
if (values != null && !values.isFlagSet() && values.getValues().length <= MAX_NUMBER_OF_BRANCHES) {
|
||||
List<Value> result = new ArrayList<>();
|
||||
for (PsiAnnotationMemberValue value : values.getValues()) {
|
||||
|
||||
Reference in New Issue
Block a user