Skip to content

Commit 1ea67ec

Browse files
committed
use callie type as hint when resolving enumValues in EnumValue.match() + minor fix BinaryExpressionAnnotator to avoid incorrect annotations in match()
1 parent d06d974 commit 1ea67ec

File tree

4 files changed

+86
-9
lines changed

4 files changed

+86
-9
lines changed

src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeBinaryExpressionAnnotator.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import com.intellij.psi.util.PsiTreeUtil;
1212
import org.jetbrains.annotations.NotNull;
1313

14+
import static com.intellij.plugins.haxe.model.evaluator.callexpression.EnumValueMatchUtil.isInsidePatternMatcher;
15+
1416
public class HaxeBinaryExpressionAnnotator implements Annotator {
1517
@Override
1618
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
@@ -50,13 +52,13 @@ public static void check(final HaxeBinaryExpression binaryExpression, final Anno
5052
if (result.isUnknown()) {
5153

5254

53-
PsiElement LeftChild = children[0];
55+
PsiElement leftChild = children[0];
5456
PsiElement rightChild = children[2];
5557

56-
HaxeGenericResolver lhsResolver = HaxeGenericResolverUtil.generateResolverFromScopeParents(LeftChild);
58+
HaxeGenericResolver lhsResolver = HaxeGenericResolverUtil.generateResolverFromScopeParents(leftChild);
5759
HaxeGenericResolver rhsResolver = HaxeGenericResolverUtil.generateResolverFromScopeParents(rightChild);
5860

59-
ResultHolder lhsType = HaxeTypeResolver.getPsiElementType(LeftChild, binaryExpression, lhsResolver);
61+
ResultHolder lhsType = HaxeTypeResolver.getPsiElementType(leftChild, binaryExpression, lhsResolver);
6062
ResultHolder rhsType = HaxeTypeResolver.getPsiElementType(rightChild, binaryExpression, rhsResolver);
6163

6264

@@ -67,8 +69,8 @@ public static void check(final HaxeBinaryExpression binaryExpression, final Anno
6769
if (lhsType.isUnknown() || rhsType.isUnknown() || containsMacroExpression) {
6870
return;
6971
}
70-
// ignoring enums as they are often "OR-ed" (|) in switch expressions (and EnumValue.match)
71-
if (lhsType.isEnum() && rhsType.isEnum()) {
72+
// ignoring enums as they are often "OR-ed" (|) in switch expressions (and EnumValue.match)
73+
if (isInsidePatternMatcher(binaryExpression) && isAllPipedEnumValues(binaryExpression)) {
7274
return;
7375
}
7476

@@ -91,4 +93,24 @@ public static void check(final HaxeBinaryExpression binaryExpression, final Anno
9193
}
9294
}
9395
}
96+
97+
private static boolean isAllPipedEnumValues(HaxeExpression expression) {
98+
if (expression instanceof HaxeBinaryExpression binaryExpression) {
99+
HaxeExpression leftExpression = binaryExpression.getLeftExpression();
100+
HaxeExpression rightExpression = binaryExpression.getRightExpression();
101+
return binaryExpression.getOperator().textMatches("|")
102+
&& isAllPipedEnumValues(leftExpression)
103+
&& isAllPipedEnumValues(rightExpression);
104+
105+
} else if (expression instanceof HaxeCallExpression callExpression) {
106+
if(callExpression.getExpression() instanceof HaxeReferenceExpression referenceExpression ) {
107+
return referenceExpression.resolve() instanceof HaxeEnumValueDeclaration;
108+
}
109+
110+
} else if (expression instanceof HaxeReferenceExpression referenceExpression) {
111+
HaxeExpressionEvaluatorContext evaluate = HaxeExpressionEvaluator.evaluate(referenceExpression);
112+
return evaluate.result.isEnumValueType();
113+
}
114+
return false;
115+
}
94116
}

src/main/java/com/intellij/plugins/haxe/lang/psi/HaxeResolver.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
import static com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceUtil.textCanBeQname;
6666
import static com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluator.findObjectLiteralType;
6767
import static com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluatorHandlers.getArrayAccessTypeFromClass;
68+
import static com.intellij.plugins.haxe.model.evaluator.callexpression.EnumValueMatchUtil.isInsidePatternMatcher;
69+
import static com.intellij.plugins.haxe.model.evaluator.callexpression.EnumValueMatchUtil.isPatternMatcher;
6870
import static com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionUtil.createContextForConstructorCall;
6971
import static com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionUtil.createContextForMethodCall;
7072
import static com.intellij.plugins.haxe.model.type.SpecificTypeReference.*;
@@ -368,16 +370,20 @@ private List<? extends PsiElement> checkElementUsage(@NotNull PsiElement referen
368370
// check Enum values
369371
if(haxeClass.isEnum()) {
370372
if(haxeClass.getModel() instanceof HaxeEnumModel enumModel) {
373+
boolean isInPatternMatcher = isInsidePatternMatcher(reference);
371374
for (HaxeEnumValueModel value : enumModel.getValues()) {
372375
if(value.getNamePsi().textMatches(reference)) {
373376
if (value instanceof HaxeEnumValueConstructorModel constructorModel) {
374377
// validate parameters if callExpression ignore if EnumExtractor
375-
if(!(reference.getParent() instanceof HaxeEnumValueReference)) {
378+
if (!(reference.getParent() instanceof HaxeEnumValueReference)) {
376379
boolean isValidConstructor = testAsEnumValueConstructor(constructorModel.getEnumValuePsi(), referenceExpression);
377-
if (!isValidConstructor) continue;
380+
if (isValidConstructor || isInPatternMatcher) {
381+
return List.of(constructorModel.getNamePsi());
382+
}
378383
}
384+
} else if (isInPatternMatcher && value instanceof HaxeEnumValueFieldModel fieldModel) {
385+
return List.of(fieldModel.getNamePsi());
379386
}
380-
return List.of(value.getNamePsi());
381387
}
382388
}
383389
}
@@ -396,6 +402,7 @@ private List<? extends PsiElement> checkElementUsage(@NotNull PsiElement referen
396402
return null;
397403
}
398404

405+
399406
// Experimental
400407
// try to step one level up until we find a type definition and then pass that back down
401408
private ResultHolder findParentAssignType(@NotNull PsiElement reference) {
@@ -516,9 +523,18 @@ private ResultHolder findParentAssignType(@NotNull PsiElement reference, boolean
516523
if (callExpression.getExpression() instanceof HaxeReferenceExpression referenceExpression) {
517524
PsiElement resolve = referenceExpression.resolve();
518525
if (resolve instanceof HaxeMethod method) {
526+
527+
// enumRef.match(enumMember) expects members from the same Enum so it should resolve members without the need of imports.
528+
if (isPatternMatcher(method)) {
529+
ResultHolder type = findTypeFromPatternMatchExpression(referenceExpression);
530+
if(type != null && type.isEnum()) return type;
531+
}
532+
519533
int index = expressionList.getExpressionList().indexOf(reference);
520534
HaxeCallExpressionEvaluation evaluate = cachedHaxeCallExpressionEvaluation(method, callExpression);
521-
return evaluate == null ? null : evaluate.getParameterType(index);
535+
if (evaluate != null && evaluate.isValid() && evaluate.isCompleted()) {
536+
return evaluate.getParameterType(index);
537+
}
522538
}
523539
}
524540
}
@@ -585,6 +601,20 @@ private ResultHolder findParentAssignType(@NotNull PsiElement reference, boolean
585601
return findParentAssignType(parent, isValueExpression);
586602
}
587603

604+
private @Nullable ResultHolder findTypeFromPatternMatchExpression(HaxeReferenceExpression referenceExpression) {
605+
HaxeReference leftReference = HaxeResolveUtil.getLeftReference(referenceExpression);
606+
if (leftReference instanceof HaxeReferenceExpression callieReference) {
607+
ResultHolder callieType = HaxeExpressionEvaluator.evaluate(callieReference).result;
608+
if (callieType != null && callieType.getType() instanceof SpecificHaxeClassReference classReference) {
609+
SpecificTypeReference resolvedType = classReference.fullyResolveTypeDefAndUnwrapNullTypeReference();
610+
if (resolvedType.isEnumReference()) return resolvedType.createHolder();
611+
}
612+
}
613+
return null;
614+
}
615+
616+
617+
588618
private static @Nullable HaxeCallExpressionEvaluation cachedHaxeCallExpressionEvaluation(HaxeMethod method, HaxeCallExpression callExpression) {
589619

590620
HaxeCallExpressionEvaluatorCacheService service = method.getProject().getService(HaxeCallExpressionEvaluatorCacheService.class);

src/main/java/com/intellij/plugins/haxe/model/evaluator/callexpression/EnumValueMatchUtil.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package com.intellij.plugins.haxe.model.evaluator.callexpression;
22

33
import com.intellij.plugins.haxe.lang.psi.*;
4+
import com.intellij.plugins.haxe.model.FullyQualifiedInfo;
45
import com.intellij.plugins.haxe.model.HaxeEnumValueFieldModel;
56
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.util.PsiTreeUtil;
68
import org.jetbrains.annotations.NotNull;
79

10+
import java.util.Objects;
11+
812
import static com.intellij.plugins.haxe.model.evaluator.callexpression.HaxeCallExpressionContext.countRequiredArguments;
913

1014
public class EnumValueMatchUtil {
@@ -52,6 +56,26 @@ private static void checkPatternArguments(HaxeCallExpressionContext context, Hax
5256
}
5357
}
5458

59+
public static boolean isInsidePatternMatcher(@NotNull PsiElement reference) {
60+
HaxeCallExpression parentOfType = PsiTreeUtil.getParentOfType(reference.getParent(), HaxeCallExpression.class);
61+
if(parentOfType != null && parentOfType.getExpression() instanceof HaxeReferenceExpression referenceExpression) {
62+
if(referenceExpression.resolve() instanceof HaxeMethod method) {
63+
return isPatternMatcher(method);
64+
}
65+
}
66+
return false;
67+
}
68+
69+
public static boolean isPatternMatcher(HaxeMethod method) {
70+
if(method == null) return false;
71+
FullyQualifiedInfo info = method.getModel().getQualifiedInfo();
72+
return info != null
73+
&& Objects.equals(info.memberName, "match")
74+
&& Objects.equals(info.className, "EnumValue")
75+
&& Objects.equals(info.moduleName, "EnumValue")
76+
&& Objects.equals(info.packagePath, "");
77+
}
78+
5579
public static HaxeCallExpressionEvaluation checkPatternMatchingOutsideMatchFunction(HaxeCallExpressionContext context, HaxeCallExpressionEvaluation evaluation, boolean trackErrors) {
5680
if (trackErrors) {
5781
for (CallExpressionArgumentModel argument : context.arguments) {

src/test/resources/testData/annotation.semantic/EnumMatchPattern.hx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class EnumPattern {
2020

2121
var match = valA.match(constructorA(_) | ValueB);
2222
var match = valA.match(constructorA(_) | constructorB(_));
23+
var match = valA.match(ValueA | constructorA(_) | ValueB | constructorB(_));
2324

2425
// wrong
2526
var match = <error descr="Unable to apply operator | for types ValueA and ValueB">ValueA | ValueB</error>; // pattern not allowed outside match

0 commit comments

Comments
 (0)