Skip to content

Commit

Permalink
Fixing issue with assign from typeParameter constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
m0rkeulv committed Jan 13, 2025
1 parent d7509c1 commit 77f7479
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public List<? extends PsiElement> resolve(@NotNull HaxeReference reference, bool

List<? extends PsiElement> elements = skipCaching ? doResolve(reference, incompleteCode)
: ResolveCache.getInstance(reference.getProject())
.resolveWithCaching(reference, this::doResolve, false, incompleteCode);
.resolveWithCaching(reference, this::doResolve, true, incompleteCode);

if (reportCacheMetrics) {
if (skipCachingForDebug) {
Expand Down Expand Up @@ -182,7 +182,7 @@ private List<? extends PsiElement> doResolve(@NotNull HaxeReference reference, b
}

private List<? extends PsiElement> doResolveInner(@NotNull HaxeReference reference, boolean incompleteCode, String referenceText) {
RecursionManager.markStack();

if (reportCacheMetrics) {
resolves.incrementAndGet();
}
Expand All @@ -195,13 +195,15 @@ private List<? extends PsiElement> doResolveInner(@NotNull HaxeReference referen
boolean isType = reference.getParent() instanceof HaxeType || PsiTreeUtil.getParentOfType(reference, HaxeTypeTag.class) != null;
List<? extends PsiElement> result = checkIsTypeParameter(reference);

if (result == null) result = checkIsChain(reference);

if (result == null) result = checkIsAlias(reference);
if (result == null) result = checkEnumMemberHints(reference);
if (result == null) result = checkIsType(reference);
if (result == null) result = checkIsFullyQualifiedStatement(reference);
if (result == null) result = checkIsSuperExpression(reference);
if (result == null) result = checkMacroIdentifier(reference);
if (result == null) result = checkIsChain(reference);
// if (result == null) result = checkIsChain(reference);
if (result == null) result = checkIsAccessor(reference);
if (result == null) result = checkIsSwitchVar(reference);
if (result == null) result = checkByTreeWalk(reference); // Beware: This will also locate constraints in scope.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,18 @@ public SpecificFunctionReference getFunctionType(@Nullable HaxeGenericResolver r

public ResultHolder getReturnType(@Nullable HaxeGenericResolver resolver) {
HaxeClassModel declaringEnum = getDeclaringEnum();

if (declaringEnum != null) {
HaxeClassReference superclassReference = new HaxeClassReference(declaringEnum, declaringEnum.haxeClass);
if (resolver != null) {
SpecificHaxeClassReference reference =
SpecificHaxeClassReference.withGenerics(superclassReference, resolver.getSpecificsFor(declaringEnum.haxeClass));

@NotNull ResultHolder[] specificsFor = resolver.getSpecificsFor(declaringEnum.haxeClass);
SpecificHaxeClassReference reference = SpecificHaxeClassReference.withGenerics(superclassReference, specificsFor);
return reference.createHolder();
} else {
SpecificHaxeClassReference reference =
SpecificHaxeClassReference.withoutGenerics(superclassReference);
return reference.createHolder();
return declaringEnum.getInstanceType();
}
}

return SpecificHaxeClassReference.getUnknown(basePsi).createHolder();
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.intellij.plugins.haxe.model.evaluator;

import com.esotericsoftware.kryo.kryo5.util.Null;
import com.intellij.lang.ASTNode;
import com.intellij.lang.annotation.AnnotationBuilder;
import com.intellij.openapi.util.RecursionGuard;
Expand Down Expand Up @@ -1370,6 +1371,9 @@ static ResultHolder handleCallExpression(
// generateResolverFromScopeParents - making sure we got typeParameters from arguments/parameters
HaxeGenericResolver localResolver = HaxeGenericResolverUtil.generateResolverFromScopeParents(callExpression);
localResolver.addAll(resolver);
if(resolver.getAssignHint() != null) {
localResolver.setAssignHint(resolver.getAssignHint());
}

SpecificTypeReference functionType;
if (callExpressionRef != null) { // can be null if the entire expression is a macro of callExpression
Expand All @@ -1386,13 +1390,22 @@ static ResultHolder handleCallExpression(
}
}

functionType = handle(callExpressionRef, context, localResolver).getType();
boolean varIsMacroFunction = isCallExpressionToMacroMethod(callExpressionRef);
boolean callIsFromMacroContext = isInMacroFunction(callExpressionRef);
if (varIsMacroFunction && !callIsFromMacroContext) {
ResultHolder holder = resolveMacroTypesForFunction(functionType.createHolder());
functionType = holder.getFunctionType();
HaxeMethodModel methodModel = tryGetMethodModel(callExpression);
if(methodModel != null) {
ResultHolder assignHint = resolver.getAssignHint();
SpecificTypeReference assignHintType = assignHint == null ? null : assignHint.getType();
HaxeCallExpressionContext callExpressionContext = HaxeCallExpressionUtil.createContextForMethodCall(callExpression, assignHintType, methodModel.getMethod());
HaxeCallExpressionEvaluation evaluate = callExpressionContext.evaluate();
functionType = evaluate.getFunctionType(methodModel);
}else {
functionType = handle(callExpressionRef, context, localResolver).getType();
}
boolean varIsMacroFunction = isCallExpressionToMacroMethod(callExpressionRef);
boolean callIsFromMacroContext = isInMacroFunction(callExpressionRef);
if (varIsMacroFunction && !callIsFromMacroContext) {
ResultHolder holder = resolveMacroTypesForFunction(functionType.createHolder());
functionType = holder.getFunctionType();
}
}else if (callExpression.getMacroExpressionReification() != null) {
functionType = SpecificTypeReference.getUnknown(callExpression.getMacroExpressionReification());
}else {
Expand Down Expand Up @@ -1537,20 +1550,27 @@ static ResultHolder handleCallExpression(
return createUnknown(callExpression);
}

private static HaxeClass tryGetMethodDeclaringClass(HaxeCallExpression expression) {
@Nullable
private static HaxeMethodModel tryGetMethodModel(HaxeCallExpression expression) {
if (expression.getExpression() instanceof HaxeReference reference) {
final PsiElement resolved = reference.resolve();
if (resolved instanceof HaxeMethod method) {
HaxeMethodModel model = method.getModel();
if(model != null) {
HaxeClassModel classModel = model.getDeclaringClass();
if(classModel != null) return classModel.haxeClass;
}
return method.getModel();
}
}
return null;
}

@Null
private static HaxeClass tryGetMethodDeclaringClass(HaxeCallExpression expression) {
HaxeMethodModel model = tryGetMethodModel(expression);
if (model != null) {
HaxeClassModel classModel = model.getDeclaringClass();
if (classModel != null) return classModel.haxeClass;
}
return null;
}

private static boolean isInMacroFunction(HaxeExpression ref) {
HaxeMethodDeclaration type = PsiTreeUtil.getParentOfType(ref, HaxeMethodDeclaration.class);
if (type != null && type.getModel() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxeComponent
// This is a common problem when you got a variable that gets its typeParameters from method calls on that instance,
// and our code will try to find callie type
var newValues = searchReferencesForTypeParametersRecursionGuard.computePreventingRecursion(componentName, false, () -> {
ResultHolder originalType = resultHolder.duplicate();
SpecificHaxeClassReference classType = originalType.getClassType();
ResultHolder updatedType = resultHolder.duplicate();
SpecificHaxeClassReference classType = updatedType.getClassType();
// TODO mlo: should we add some kind of support for functions here ?
if (classType == null) return originalType;
if (classType == null) return updatedType;

HaxeGenericResolver classResolver = classType.getGenericResolver();
PsiSearchHelper searchHelper = PsiSearchHelper.getInstance(componentName.getProject());
Expand Down Expand Up @@ -136,14 +136,14 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxeComponent
PsiElement parent = expression.getParent();

if (reference instanceof HaxeReferenceExpression referenceExpression) {
ResultHolder result = tryFindTypeWhenUsedAsParameterInCallExpression(originalType, referenceExpression, parent);
ResultHolder result = tryFindTypeWhenUsedAsParameterInCallExpression(updatedType, referenceExpression, parent);
if (result == null) return null;
if (!result.isUnknown()) originalType = mapTypeParameterIfAssignable(originalType, result);
if (!originalType.containsUnknownTypes()) return originalType;
if (!result.isUnknown()) updatedType = mapTypeParameterIfAssignable(updatedType, result);
if (!updatedType.containsUnknownTypes()) return updatedType;
}

if (parent instanceof HaxeAssignExpression assignExpression) {
ResultHolder assignType = tryTypeFromAssignExpression(context, resolver, originalType, assignExpression, componentName);
ResultHolder assignType = tryTypeFromAssignExpression(context, resolver, updatedType, assignExpression, componentName);
if (assignType == null) return null;
if (!assignType.isUnknown()) {
// we want to ignore assign to null value (flag to not change isFirst)
Expand All @@ -160,50 +160,50 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxeComponent
}
}
if (isRightExpresion) {
ResultHolder instanceType = getInstanceTypeWithDefaultTypeParameters(originalType);
ResultHolder instanceType = getInstanceTypeWithDefaultTypeParameters(updatedType);
if (instanceType.canAssign(assignType)) {
originalType = mapTypeParameter(originalType, assignType);
updatedType = mapTypeParameter(updatedType, assignType);
}
} else {
if (assignType.canAssign(originalType)) {
originalType = mapTypeParameter(originalType, assignType);
if (assignType.canAssign(updatedType)) {
updatedType = mapTypeParameter(updatedType, assignType);
}
}
}
}
if (!originalType.containsUnknownTypes()) return originalType;
if (!updatedType.containsUnknownTypes()) return updatedType;
}

if (parent instanceof HaxeReferenceExpression referenceExpression) {
ResultHolder result = tryFindTypeFromMethodCallOnReference(originalType, referenceExpression);
ResultHolder result = tryFindTypeFromMethodCallOnReference(updatedType, referenceExpression);
if (result == null) return null;
if (!result.isUnknown()) originalType = mapTypeParameterIfAssignable(originalType, result);
if (!originalType.containsUnknownTypes()) return originalType;
if (!result.isUnknown()) updatedType = mapTypeParameterIfAssignable(updatedType, result);
if (!updatedType.containsUnknownTypes()) return updatedType;
}

if (parent instanceof HaxeObjectLiteralElement literalElement) {
ResultHolder result = tryTypeFromObjectLiteral(context, resolver, literalElement);
if (result == null) return null;
if (!result.isUnknown()) originalType = mapTypeParameterIfAssignable(originalType, result);
if (!originalType.containsUnknownTypes()) return originalType;
if (!result.isUnknown()) updatedType = mapTypeParameterIfAssignable(updatedType, result);
if (!updatedType.containsUnknownTypes()) return updatedType;
}

if (parent instanceof HaxeArrayAccessExpression arrayAccessExpression) {
ResultHolder result = tryUpdateTypeParamFromArrayAccess(context, resolver, arrayAccessExpression, classType, classResolver, classType);
if (result == null) return null;
if (!result.isUnknown()) originalType = mapTypeParameterIfAssignable(originalType, result);
if (!originalType.containsUnknownTypes()) return originalType;
if (!result.isUnknown()) updatedType = mapTypeParameterIfAssignable(updatedType, result);
if (!updatedType.containsUnknownTypes()) return updatedType;
}

if (parent instanceof HaxeObjectLiteralElement literalElement) {
ResultHolder result = tryUpdateTypeParamFromObjectLiteral(context, resolver, literalElement, classType);
if (result == null) return null;
if (!result.isUnknown()) originalType = mapTypeParameterIfAssignable(originalType, result);
if (!originalType.containsUnknownTypes()) return originalType;
if (!result.isUnknown()) updatedType = mapTypeParameterIfAssignable(updatedType, result);
if (!updatedType.containsUnknownTypes()) return updatedType;
}
}
}
return originalType;
return updatedType;
});
return newValues != null ? newValues.noCache() : resultHolder.noCache();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class HaxeCallExpressionContext {
@Setter
@Nullable
SpecificHaxeClassReference callie;
SpecificTypeReference assignHint;

@Nullable
private PsiElement sourceExpression;
Expand Down Expand Up @@ -125,6 +126,8 @@ private HaxeCallExpressionEvaluation evaluate(boolean trackErrors, PsiElement so
parameterResolver.addAll(methodResolver);
parameterResolver.addAll(callExpressionScopeResolver);

applyAssignHint(argumentResolver, parameterResolver);

// we use a third resolver that combines callie and method parameter values to correctly resolve parameter type
// we do this because we want the parameter resolver to only keep track the values used in the callExpression
// and not be affected by callie values, this is important to correctly resolve returnType.
Expand Down Expand Up @@ -243,11 +246,27 @@ private HaxeCallExpressionEvaluation evaluate(boolean trackErrors, PsiElement so

}
// update callExpressionResolver with any new resolve values from argument-parameter types
evaluation.callExpressionResolver.addAll(parameterResolver);
evaluation.callExpressionResolver.addAll(combinedResolver);
evaluation.setCompleted(true);
return evaluation;
}

private void applyAssignHint(HaxeGenericResolver argumentResolver, HaxeGenericResolver parameterResolver) {
if (assignHint != null && returnType != null) {
SpecificTypeReference _assignHint = assignHint;
if(_assignHint instanceof SpecificHaxeClassReference classReference) {
_assignHint = classReference.fullyResolveTypeDefAndUnwrapNullTypeReference();
}

SpecificTypeReference _returnType = returnType.getType();
if(_returnType instanceof SpecificHaxeClassReference classReference) {
_returnType = classReference.fullyResolveTypeDefAndUnwrapNullTypeReference();
}

updateResolverIfNecessary(_assignHint, argumentResolver,_returnType, parameterResolver);
}
}

private static @NotNull HaxeGenericResolver getCallieResolver(SpecificTypeReference resolvedCallie) {
return Optional.ofNullable(resolvedCallie)
.filter(s -> s instanceof SpecificHaxeClassReference)
Expand All @@ -268,7 +287,9 @@ private void updateResolverIfNecessary(SpecificTypeReference argumentType, HaxeG
if(parameterResolver.containsConstraint(typeParameter)) {
ResultHolder resolve = parameterResolver.resolve(typeParameter);
if (resolve == null || resolve.isUnknown() || resolve.isTypeParameter()) {
parameterResolver.add(typeParameter, argumentType.createHolder());
if (parameterClassReference.canAssign(argumentType)) {
parameterResolver.add(typeParameter, argumentType.createHolder());
}
}
}
}
Expand All @@ -280,9 +301,9 @@ else if (parameterClassReference.createHolder().containsUnknownTypeParameters())
@NotNull ResultHolder[] argumentSpecifics = downCastedType.getSpecifics();
int argumentsToCheck = Math.min(argumentSpecifics.length, parameterSpecifics.length);
for (int i = 0; i < argumentsToCheck; i++) {
ResultHolder parameterSpecific = parameterSpecifics[i];
ResultHolder argumentSpecific = argumentResolver.resolve(argumentSpecifics[i]);
if (argumentSpecific != null) {
ResultHolder parameterSpecific = tryUnwrapNull(parameterSpecifics[i]);
ResultHolder argumentSpecific = tryUnwrapNull(argumentResolver.resolve(argumentSpecifics[i]));
if (argumentSpecific != null && parameterSpecific.canAssign(argumentSpecific)) {
updateResolverIfNecessary(
argumentSpecific.getType(), argumentResolver,
parameterSpecific.getType(), parameterResolver);
Expand Down Expand Up @@ -334,6 +355,14 @@ else if (parameterClassReference.createHolder().containsUnknownTypeParameters())
}
}

private ResultHolder tryUnwrapNull(@Nullable ResultHolder holder) {
if(holder == null) return null;
if(holder.isNullWrappedType()) {
return holder.getClassType().unwrapNullType().createHolder();
}
return holder;
}

private static @NotNull SpecificTypeReference tryResolve(HaxeGenericResolver callExpressionResolver, SpecificTypeReference type) {
ResultHolder resolvedParameterType = callExpressionResolver.resolve(type);
if (resolvedParameterType != null && !resolvedParameterType.isUnknown()) {
Expand Down
Loading

0 comments on commit 77f7479

Please sign in to comment.