Skip to content

Commit

Permalink
add enum hints for object literals
Browse files Browse the repository at this point in the history
  • Loading branch information
m0rkeulv committed Aug 11, 2024
1 parent bb874ad commit c2eb8ca
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 24 deletions.
36 changes: 31 additions & 5 deletions src/main/java/com/intellij/plugins/haxe/lang/psi/HaxeResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import static com.intellij.plugins.haxe.model.type.HaxeExpressionEvaluator.findObjectLiteralType;
import static com.intellij.plugins.haxe.util.HaxeDebugLogUtil.traceAs;
import static com.intellij.plugins.haxe.util.HaxeStringUtil.elide;

Expand Down Expand Up @@ -370,13 +371,33 @@ private List<? extends PsiElement> checkEnumMemberHints(HaxeReference reference)
}
}
}
if(referenceParent instanceof HaxeObjectLiteralElement literalElement) {
HaxeObjectLiteral objectLiteral = PsiTreeUtil.getParentOfType(literalElement, HaxeObjectLiteral.class);
if(objectLiteral != null) {
ResultHolder objectLiteralType = findObjectLiteralType(new HaxeExpressionEvaluatorContext(objectLiteral), null, objectLiteral);
if(objectLiteralType != null && !objectLiteralType.isUnknown()) {
SpecificHaxeClassReference typeFromUsage = objectLiteralType.getClassType();
if (typeFromUsage != null && typeFromUsage.getHaxeClassModel() != null) {
HaxeBaseMemberModel objectLiteralElementAsMember = typeFromUsage.getHaxeClassModel()
.getMember(literalElement.getName(), typeFromUsage.getGenericResolver());
if(objectLiteralElementAsMember != null) {
ResultHolder type = objectLiteralElementAsMember.getResultType(typeFromUsage.getGenericResolver());
if(type != null && type.isEnum()) {
List<HaxeComponentName> components = findEnumMember(reference, type.getType());
if (components != null) return components;
}

}
}
}
}
}

if (referenceParent instanceof HaxeCallExpression) {
List<HaxeComponentName> member = checkParameterListFromCallExpressions(reference, referenceParent);
if (member != null) return member;
}


if (!(referenceParent instanceof HaxeType)) {
HaxeParameter parameterFromReferenceExpression = null;
HaxePsiField fieldFromReferenceExpression = null;
Expand Down Expand Up @@ -811,11 +832,12 @@ private List<? extends PsiElement> checkIsSwitchVar(HaxeReference reference) {

HaxeSwitchCaseExpr switchCaseExpr = PsiTreeUtil.getParentOfType(reference, HaxeSwitchCaseExpr.class);

List<? extends PsiElement> result = null;
// check "case" scope for local variables first before we go higher up and check the case expression
List<? extends PsiElement> result = checkByTreeWalk(reference, switchCaseExpr);

// NOTE: this one has to come before `checkIfSwitchCaseDefaultValue`
// check if default name in match expression (ex `case TString(_ => captureVar)`)
result = checkIfDefaultValueInMatchExpression(reference, switchCaseExpr);
if (result == null) result = checkIfDefaultValueInMatchExpression(reference, switchCaseExpr);

// check if enum extracted value (ex `case MyEnumVal(reference)`)
if (result == null) result = checkIsSwitchExtractedValue(reference);
Expand Down Expand Up @@ -1199,13 +1221,17 @@ private List<? extends PsiElement> checkIsForwardedName(HaxeReference reference)
* @param reference
* @return
*/
private List<? extends PsiElement> checkByTreeWalk(HaxeReference reference) {
private List<? extends PsiElement> checkByTreeWalk(HaxeReference reference, @Nullable PsiElement maxScope) {
final List<PsiElement> result = new ArrayList<>();
PsiTreeUtil.treeWalkUp(new ResolveScopeProcessor(result, reference.getText(), reference), reference, null, new ResolveState());
PsiTreeUtil.treeWalkUp(new ResolveScopeProcessor(result, reference.getText(), reference), reference, maxScope, new ResolveState());
if (result.isEmpty()) return null;
LogResolution(reference, "via tree walk.");
return result;
}
private List<? extends PsiElement> checkByTreeWalk(HaxeReference reference) {
return checkByTreeWalk(reference, (PsiElement)null);
}

private List<? extends PsiElement> checkByTreeWalk(HaxeReference scope, String name) {
final List<PsiElement> result = new ArrayList<>();
PsiTreeUtil.treeWalkUp(new ResolveScopeProcessor(result, name, null), scope, null, new ResolveState());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,9 @@ public static List<PsiReference> referenceSearch(final HaxeComponentName compone
}).toList();
}

private static final RecursionGuard<PsiElement>
checkSearchResultRecursionGuard = RecursionManager.createGuard("EvaluatorCheckSearchResultRecursionGuard");

@Nullable
private static ResultHolder checkSearchResult(HaxeExpressionEvaluatorContext context, HaxeGenericResolver resolver, PsiReference reference,
HaxeComponentName originalComponent,
Expand Down Expand Up @@ -611,10 +614,27 @@ private static ResultHolder checkSearchResult(HaxeExpressionEvaluatorContext con
if (expression.getParent() instanceof HaxeObjectLiteralElement literalElement) {
HaxeObjectLiteral objectLiteral = PsiTreeUtil.getParentOfType(literalElement, HaxeObjectLiteral.class);
if(objectLiteral != null) {
ResultHolder objectLiteralType = findObjectLiteralType(context, resolver, objectLiteral);
if (objectLiteralType != null && !objectLiteralType.isUnknown()) {
return objectLiteralType;
}
ResultHolder result = checkSearchResultRecursionGuard.computePreventingRecursion(objectLiteral, false, () -> {
ResultHolder objectLiteralType = findObjectLiteralType(context, resolver, objectLiteral);
if (objectLiteralType != null && !objectLiteralType.isUnknown()) {
SpecificHaxeClassReference classType = objectLiteralType.getClassType();
if (classType != null) {
HaxeClassModel classModel = classType.getHaxeClassModel();
if (classModel != null) {
HaxeGenericResolver genericResolver = classType.getGenericResolver();
HaxeBaseMemberModel member = classModel.getMember(literalElement.getName(), genericResolver);
if (member != null) {
ResultHolder type = member.getResultType(genericResolver);
if (type != null && !type.isUnknown()) {
return type;
}
}
}
}
}
return null;
});
if(result != null) return result;
}
}
}
Expand Down Expand Up @@ -869,15 +889,21 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxePsiField
return resultHolder;
}

private static @Nullable ResultHolder findObjectLiteralType(HaxeExpressionEvaluatorContext context,
public static @Nullable ResultHolder findObjectLiteralType(HaxeExpressionEvaluatorContext context,
HaxeGenericResolver resolver,
HaxeObjectLiteral objectLiteral) {
ResultHolder objectLiteralType = null;

// we need to find where the literal is used to find correct type
if (objectLiteral.getParent() instanceof HaxeAssignExpression assignExpression) {
objectLiteralType = handleWithRecursionGuard(assignExpression.getLeftExpression(), context, resolver);

}
if (objectLiteral.getParent() instanceof HaxeVarInit varInit) {
HaxePsiField field = PsiTreeUtil.getParentOfType(varInit, HaxePsiField.class);
if(field != null) {
ResultHolder fieldType = HaxeTypeResolver.getTypeFromTypeTag(field.getTypeTag(), field);
if(!fieldType.isUnknown()) return fieldType;
}
}
if (objectLiteral.getParent().getParent() instanceof HaxeCallExpression callExpression) {
if (callExpression.getExpression() instanceof HaxeReference callExpressionReference) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1466,11 +1466,12 @@ static ResultHolder handleLocalVarDeclaration(
ResultHolder searchResult = evaluatorHandlersRecursionGuard.computePreventingRecursion(element, true, () -> {
return searchReferencesForType(element, context, resolver, null, hint);
});
// if we got a type we should check that we find the correct match (avoid replacing a class with an interface match etc.)
if (searchResult != null) {
if (result == null || searchResult.getType().isSameType(result.getType())) {
result = searchResult;
}
if (result == null) {
result = searchResult;
}else if (searchResult.getType().isSameType(result.getType())) {
result = HaxeTypeUnifier.unify(result, searchResult);
}
}
}
result = tryGetEnumValuesDeclaringClass(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,10 @@ static private boolean canAssignToFromType(
if(to.getHaxeClassModel() != null && !to.isTypeParameter()) { // typeParameters can be Anonymous but is not part of the anonymous "interface" check here
HaxeClassModel classModel = to.getHaxeClassModel();
if (classModel.isAnonymous() || classModel.isStructInit() || classModel.isObjectLiteral()){
// compare members (Anonymous stucts can be "used" as interface)
return containsAllMembers(to, from, context);
if (!from.isTypeParameter()) { // cant get members from a typeParameter
// compare members (Anonymous stucts can be "used" as interface)
return containsAllMembers(to, from, context);
}
}
}

Expand Down Expand Up @@ -583,12 +585,6 @@ private static boolean containsAllMembers(SpecificHaxeClassReference to, Specifi
Boolean canAssign = containsMembersRecursionGuard.computePreventingRecursion(context.getFromOrigin(), false, () -> {
ResultHolder toType = toMember.getResultType();
ResultHolder fromType = fromMember.getResultType();

//TODO, temp hack ignoring type of enum until better resolve logic is added
SpecificTypeReference type = fromType.getType();
boolean fromIsEnum = type.isEnumValue() || type.isEnumType() || type.isEnumClass() || type.isEnumValueClass();
if (toType.isEnum() && fromIsEnum) return true;

return toType.canAssign(fromType);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,9 @@ else if (unwrapped instanceof SpecificFunctionReference functionReference) {
public SpecificTypeReference fullyResolveUnderlyingTypeUnwrapNullTypeReference() {
SpecificTypeReference result = fullyresolveRecursionGuard.computePreventingRecursion(this.context, true, () -> {
SpecificTypeReference reference = this;
while (reference != null) {
SpecificTypeReference oldRef = null;
while (reference != null && reference != oldRef) {
oldRef = reference; // not a proper solution but should prevent 1 level of same reference infinite loop
if (reference instanceof SpecificHaxeClassReference cs) {
if (cs.isNullType() || cs.isTypeDef()) {
reference = fullyResolveTypeDefAndUnwrapNullTypeReference();
Expand Down

0 comments on commit c2eb8ca

Please sign in to comment.