Skip to content

Commit

Permalink
Fix incorrect generics for function references from inherited Methods…
Browse files Browse the repository at this point in the history
…. (+ Fix issue where evaluation cache did not clear correctly)
  • Loading branch information
m0rkeulv committed Sep 8, 2024
1 parent a8400d2 commit 19c33e0
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,13 @@ public SpecificHaxeClassReference getUnderlyingClassReference(@NotNull HaxeGener
HaxeAnonymousType anon = typeOrAnon.getAnonymousType();
if (anon != null) {
// Anonymous types don't have parameters of their own, but when they are part of a typedef, they use the parameters from it.
if(element instanceof HaxeTypedefDeclaration typedefDeclaration && typedefDeclaration.isGeneric()) {
HaxeGenericResolver memberResolver = typedefDeclaration.getMemberResolver(null);
if(memberResolver != null) {
HaxeClassReference classReference = new HaxeClassReference(anon.getModel(), element);
return SpecificHaxeClassReference.withGenerics(classReference, memberResolver.getSpecificsFor(classReference), element);
}
}
return SpecificHaxeClassReference.withGenerics(new HaxeClassReference(anon.getModel(), element), resolver.getSpecifics(), element);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.intellij.plugins.haxe.model.type;

import com.intellij.openapi.project.Project;
import com.intellij.psi.util.PsiModificationTracker;

/**
* TMP workaround for a problem
* <p>
* Looks like project serivce and project listener entries in plugin.xml creates different instances
* so to avoid problems with caches not beeing cleared we create a listener that finds the service and clears the cache.
*/
public class HaxeExpressionEvaluatorCacheChangeListener implements PsiModificationTracker.Listener {
private final Project myProject;

public HaxeExpressionEvaluatorCacheChangeListener(Project project) {
myProject = project;
}

public void modificationCountChanged() {
myProject.getService(HaxeExpressionEvaluatorCacheService.class).clearCaches();
}
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.intellij.plugins.haxe.model.type;

import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiModificationTracker;
import org.jetbrains.annotations.NotNull;

import java.util.Map;
Expand All @@ -15,15 +14,17 @@
* contains SpecificTypeReference elements both as the type and as generics and these contain PsiElements
* that might become invalid
*/
public class HaxeExpressionEvaluatorCacheService implements PsiModificationTracker.Listener {

private final Map<EvaluationKey, ResultHolder> cacheMap = new ConcurrentHashMap<>();
public class HaxeExpressionEvaluatorCacheService {

private volatile Map<EvaluationKey, ResultHolder> cacheMap = new ConcurrentHashMap<>();
public boolean skipCaching = false;

public @NotNull ResultHolder handleWithResultCaching(final PsiElement element,
final HaxeExpressionEvaluatorContext context,
final HaxeGenericResolver resolver) {

if(skipCaching) return _handle(element, context, resolver);

EvaluationKey key = new EvaluationKey(element, resolver == null ? "NO_RESOLVER" : resolver.toCacheString());
if (cacheMap.containsKey(key)) {
return cacheMap.get(key);
Expand All @@ -37,15 +38,11 @@ public class HaxeExpressionEvaluatorCacheService implements PsiModificationTrack
}
}

@Override
public void modificationCountChanged() {
clearCaches();
}

private void clearCaches() {
public void clearCaches() {
cacheMap.clear();
}
}

record EvaluationKey(PsiElement element, String evalParamString) {
record EvaluationKey( PsiElement element, String evalParamString) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -1235,8 +1235,8 @@ static ResultHolder handleCallExpression(


// map type Parameters to methods declaring class resolver if necessary
SpecificHaxeClassReference callyClassRef = tryGetCallieType(callExpression).getClassType();
HaxeClass callieType = callyClassRef != null ? callyClassRef.getHaxeClass() : null;
SpecificHaxeClassReference callieClassRef = tryGetCallieType(callExpression).getClassType();
HaxeClass callieType = callieClassRef != null ? callieClassRef.getHaxeClass() : null;
HaxeClass methodTypeClassType = tryGetMethodDeclaringClass(callExpression);
if(callieType != null && methodTypeClassType != null) {
localResolver = HaxeGenericResolverUtil.createInheritedClassResolver(methodTypeClassType,callieType, localResolver);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,14 @@ private static void mapTypeParametersFunction(Map<String, ResultHolder> map, Spe
}
}


public static HaxeGenericResolver createInheritedClassResolver(HaxeClass inheritedClass, HaxeClass ownerClass,
public static HaxeGenericResolver createInheritedClassResolver(HaxeClass targetClass, HaxeClass currentClass,
HaxeGenericResolver localResolver) {

List<SpecificHaxeClassReference> path = new ArrayList<>();
findClassHierarchy(ownerClass, inheritedClass, path);
findClassHierarchy(currentClass, targetClass, path);

Collections.reverse(path);
HaxeGenericResolver resolver = ownerClass.getMemberResolver(localResolver);
HaxeGenericResolver resolver = currentClass.getMemberResolver(localResolver);
for (SpecificHaxeClassReference reference : path) {
ResultHolder resolved = resolver.resolve(reference.createHolder());
resolver = resolved.getClassType().getGenericResolver();
Expand All @@ -279,7 +278,7 @@ private static boolean findClassHierarchy(HaxeClass from, HaxeClass to, List<Spe

HaxeClassModel fromModel = from.getModel();
if (fromModel.isTypedef()) {
SpecificHaxeClassReference reference = fromModel.getUnderlyingClassReference(new HaxeGenericResolver());
SpecificHaxeClassReference reference = fromModel.getUnderlyingClassReference(fromModel.getGenericResolver(null));
if (reference!= null) {
path.add(reference);
SpecificHaxeClassReference resolvedTypeDef = reference.resolveTypeDefClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ static public ResultHolder resolveParameterizedType(@NotNull ResultHolder result
// Resolve any generics on the resolved type as well.
typeReference = result.getType();
if (typeReference instanceof SpecificHaxeClassReference classReference) {
RecursionManager.markStack();
ResultHolder holder = propagateRecursionGuard.computePreventingRecursion(result, true, () ->
SpecificHaxeClassReference.propagateGenericsToType(classReference.createHolder(), finalResolver, returnType));
if (holder != null) result = holder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,19 @@ public ResultHolder access(String name, HaxeExpressionEvaluatorContext context,
localResolver.addAll(result.getGenericResolver());
}
}
HaxeNamedComponent method = aClass.findHaxeMethodByName(name, localResolver);
if (method != null) {
HaxeNamedComponent namedComponent = aClass.findHaxeMethodByName(name, localResolver);
if (namedComponent instanceof HaxeMethod method) {
if (context.root == method) return null;
if (isMacroMethod(method)) {
// if macro method replace Expr / ExprOf types
ResultHolder functionType = HaxeTypeResolver.getMethodFunctionType(method, localResolver.withoutUnknowns());
return HaxeMacroUtil.resolveMacroTypesForFunction(functionType);
}
// if inherited method map resolver to match declaring class
if(method.getContainingClass() instanceof HaxeClass methodTypeClassType){
localResolver = HaxeGenericResolverUtil.createInheritedClassResolver(methodTypeClassType, clazz, localResolver);
}

return HaxeTypeResolver.getMethodFunctionType(method, localResolver);
}

Expand Down
7 changes: 5 additions & 2 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,13 @@
topic="com.intellij.openapi.project.ProjectManagerListener"/>
<listener class="com.intellij.plugins.haxe.haxelib.HaxelibModuleManagerService"
topic="com.intellij.openapi.project.ModuleListener"/>
<listener class="com.intellij.plugins.haxe.model.type.HaxeExpressionEvaluatorCacheService"
topic="com.intellij.psi.util.PsiModificationTracker$Listener"/>

</applicationListeners>

<projectListeners>
<listener class="com.intellij.plugins.haxe.model.type.HaxeExpressionEvaluatorCacheChangeListener"
topic="com.intellij.psi.util.PsiModificationTracker$Listener"/>
</projectListeners>

<actions>
<action id="Haxe.NewHaxeClass" class="com.intellij.plugins.haxe.ide.actions.CreateClassAction"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ class Level3Class<T:String> extends Level2Class {
var paramB:Int = testTypeParam(1);
var fieldA:Int = this.field;
var fieldB:Int = field;
var fnA:Int -> Int = this.testTypeParam;

// WRONG
var <error descr="Incompatible type: Int should be String">paramA:String = this.testTypeParam(<error descr="Type mismatch (Expected: 'Int' got: 'String')">"1"</error>)</error>;
var <error descr="Incompatible type: Int should be String">paramB:String = testTypeParam(<error descr="Type mismatch (Expected: 'Int' got: 'String')">"1"</error>)</error>;
var <error descr="Incompatible type: Int should be String">fieldA:String = this.field</error>;
var <error descr="Incompatible type: Int should be String">fieldB:String = field</error>;
var <error descr="Incompatible type: Int->Int should be String->String">fnA:String -> String = this.testTypeParam</error>;

}
}
Expand Down

0 comments on commit 19c33e0

Please sign in to comment.