From 19c33e03e40b589959ab7a223d2954fec6194da3 Mon Sep 17 00:00:00 2001 From: m0rkeulv Date: Sun, 8 Sep 2024 18:12:44 +0200 Subject: [PATCH] Fix incorrect generics for function references from inherited Methods. (+ Fix issue where evaluation cache did not clear correctly) --- .../plugins/haxe/model/HaxeClassModel.java | 7 ++++++ ...xpressionEvaluatorCacheChangeListener.java | 23 +++++++++++++++++++ .../HaxeExpressionEvaluatorCacheService.java | 17 ++++++-------- .../type/HaxeExpressionEvaluatorHandlers.java | 4 ++-- .../model/type/HaxeGenericResolverUtil.java | 9 ++++---- .../haxe/model/type/HaxeTypeResolver.java | 1 - .../type/SpecificHaxeClassReference.java | 9 ++++++-- src/main/resources/META-INF/plugin.xml | 7 ++++-- .../MultiLevelGenericInheritance.hx | 2 ++ 9 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorCacheChangeListener.java diff --git a/src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java b/src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java index e421bcf71..0b47e486a 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java +++ b/src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java @@ -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); } } diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorCacheChangeListener.java b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorCacheChangeListener.java new file mode 100644 index 000000000..b5a29693f --- /dev/null +++ b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorCacheChangeListener.java @@ -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 + *

+ * 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(); + } +} + diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorCacheService.java b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorCacheService.java index 687b096f0..a65c805b3 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorCacheService.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorCacheService.java @@ -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; @@ -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 cacheMap = new ConcurrentHashMap<>(); +public class HaxeExpressionEvaluatorCacheService { + private volatile Map 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); @@ -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) { } \ No newline at end of file diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorHandlers.java b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorHandlers.java index d4949c3b0..23a932106 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorHandlers.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorHandlers.java @@ -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); diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeGenericResolverUtil.java b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeGenericResolverUtil.java index 0f4e430e9..6be2539a0 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeGenericResolverUtil.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeGenericResolverUtil.java @@ -257,15 +257,14 @@ private static void mapTypeParametersFunction(Map map, Spe } } - - public static HaxeGenericResolver createInheritedClassResolver(HaxeClass inheritedClass, HaxeClass ownerClass, + public static HaxeGenericResolver createInheritedClassResolver(HaxeClass targetClass, HaxeClass currentClass, HaxeGenericResolver localResolver) { List 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(); @@ -279,7 +278,7 @@ private static boolean findClassHierarchy(HaxeClass from, HaxeClass to, List SpecificHaxeClassReference.propagateGenericsToType(classReference.createHolder(), finalResolver, returnType)); if (holder != null) result = holder; diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/SpecificHaxeClassReference.java b/src/main/java/com/intellij/plugins/haxe/model/type/SpecificHaxeClassReference.java index e2f430cda..2c75c64a5 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/SpecificHaxeClassReference.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/SpecificHaxeClassReference.java @@ -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); } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index bea622f5b..372182a88 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -500,10 +500,13 @@ topic="com.intellij.openapi.project.ProjectManagerListener"/> - + + + + extends Level2Class { var paramB:Int = testTypeParam(1); var fieldA:Int = this.field; var fieldB:Int = field; + var fnA:Int -> Int = this.testTypeParam; // WRONG var paramA:String = this.testTypeParam("1"); var paramB:String = testTypeParam("1"); var fieldA:String = this.field; var fieldB:String = field; + var fnA:String -> String = this.testTypeParam; } }