diff --git a/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeCallExpressionUtil.java b/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeCallExpressionUtil.java index 9c67dc452..490e3e43c 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeCallExpressionUtil.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeCallExpressionUtil.java @@ -27,6 +27,9 @@ public class HaxeCallExpressionUtil { // (amongst the problem is mixed parameter classes and method needing reference for type resolve) @NotNull public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpression callExpression, @NotNull HaxeMethod method) { + return checkMethodCall(callExpression, method, false); + } + public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpression callExpression, @NotNull HaxeMethod method, boolean isFirstRef) { CallExpressionValidation validation = new CallExpressionValidation(); validation.isMethod = true; @@ -99,6 +102,12 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi } } } + if(callieType.isUnknown() && !validation.isStaticExtension && !isFirstRef ) { + //TODO hack? + // if callie is unknown we prohibit class TypeParameters unless first reference flag + // reason: we need to prevent incorrect typeParameters when evaluating monomorphs and recursion guards can cause callie to be null + validation.unknownCallie = true; + } HaxeGenericResolver resolver = HaxeGenericResolverUtil.appendCallExpressionGenericResolver(callExpression, classTypeResolver); int parameterCounter = 0; @@ -120,7 +129,7 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi // when resolving parameters HaxeGenericResolver parameterResolver = resolver.withoutUnknowns(); - TypeParameterTable typeParamTable = createTypeParameterConstraintTable(method, resolver); + TypeParameterTable typeParamTable = createTypeParameterConstraintTable(method, resolver, validation.unknownCallie); if (validation.isStaticExtension) { // this might not work for literals, need to handle those in a different way @@ -519,7 +528,7 @@ public static CallExpressionValidation checkConstructor(HaxeNewExpression newExp resolver = HaxeGenericResolverUtil.appendCallExpressionGenericResolver(newExpression, resolver); - TypeParameterTable typeParamTable = createTypeParameterConstraintTable(constructor.getMethod(), resolver); + TypeParameterTable typeParamTable = createTypeParameterConstraintTable(constructor.getMethod(), resolver, validation.unknownCallie); int parameterCounter = 0; @@ -964,6 +973,7 @@ private static boolean isExternRestClass(SpecificHaxeClassReference classReferen @Data public static class CallExpressionValidation { + public boolean unknownCallie = false; Map argumentToParameterIndex = new HashMap<>(); Map argumentIndexToType = new HashMap<>(); Map parameterIndexToType = new HashMap<>(); diff --git a/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/TypeParameterUtil.java b/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/TypeParameterUtil.java index 43ea3f984..c18d4db27 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/TypeParameterUtil.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/TypeParameterUtil.java @@ -18,7 +18,8 @@ public class TypeParameterUtil { @NotNull - public static TypeParameterTable createTypeParameterConstraintTable(HaxeMethod method, HaxeGenericResolver resolver) { + public static TypeParameterTable createTypeParameterConstraintTable(HaxeMethod method, HaxeGenericResolver resolver, + boolean prohibitClassTypeParameters) { TypeParameterTable typeParamTable = new TypeParameterTable(); @@ -31,7 +32,7 @@ public static TypeParameterTable createTypeParameterConstraintTable(HaxeMethod m } if (method.isConstructor()) { HaxeClassModel declaringClass = method.getModel().getDeclaringClass(); - if (declaringClass != null) { + if (declaringClass != null && !prohibitClassTypeParameters) { params = declaringClass.getGenericParams(); for (HaxeGenericParamModel model : params) { ResultHolder constraint = model.getConstraint(resolver); @@ -47,7 +48,9 @@ public static TypeParameterTable createTypeParameterConstraintTable(HaxeMethod m ResultHolder constraint = model.getConstraint(resolver); // make sure we do not add if method type parameter with the same name is present if(!typeParamTable.contains(model.getName(), ResolveSource.METHOD_TYPE_PARAMETER)) { - typeParamTable.put(model.getName(), constraint, ResolveSource.CLASS_TYPE_PARAMETER); + if(!prohibitClassTypeParameters) { + typeParamTable.put(model.getName(), constraint, ResolveSource.CLASS_TYPE_PARAMETER); + } } } } diff --git a/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluator.java b/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluator.java index c17b8fb50..be8f9379f 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluator.java +++ b/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluator.java @@ -46,6 +46,7 @@ import static com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluatorHandlers.*; import static com.intellij.plugins.haxe.model.evaluator.HaxeExpressionEvaluatorHandlers.handleWithRecursionGuard; import static com.intellij.plugins.haxe.model.evaluator.HaxeExpressionUsageUtil.findUsageAsParameterInFunctionCall; +import static com.intellij.plugins.haxe.model.evaluator.HaxeExpressionUsageUtil.searchReferencesForTypeParameters; import static com.intellij.plugins.haxe.model.type.HaxeMacroTypeUtil.getTypeDefinition; @CustomLog @@ -114,7 +115,7 @@ private static void cleanUp() { // keep package protected, don't use this one outside code running from evaluate() // if used outside it can mess up the result cache @NotNull - static ResultHolder handle(final PsiElement element, + static ResultHolder handle(@NotNull final PsiElement element, final HaxeExpressionEvaluatorContext context, final HaxeGenericResolver resolver) { try { @@ -125,7 +126,7 @@ static ResultHolder handle(final PsiElement element, } catch (NullPointerException e) { // Make sure that these get into the log, because the GeneralHighlightingPass swallows them. - log.error("Error evaluating expression type for element " + element.toString(), e); + log.error("Error evaluating expression type for element " + element, e); throw e; } catch (ProcessCanceledException e) { @@ -141,7 +142,9 @@ static ResultHolder handle(final PsiElement element, return createUnknown(element != null ? element : context.root); } - @NotNull + // if recursion guard is triggered value will be null + // this is intentional as providing an unknown result instead can break monomorphs + @Nullable static ResultHolder _handle(final PsiElement element, final HaxeExpressionEvaluatorContext context, HaxeGenericResolver optionalResolver) { @@ -246,7 +249,8 @@ static ResultHolder _handle(final PsiElement element, if (element instanceof HaxeReference) { if (element instanceof HaxeNewExpression expression) { - return handleNewExpression(context, resolver, expression); + ResultHolder holder = handleNewExpression(context, resolver, expression); + return findMissingTypeParametersIfNecessary(context, resolver, expression, holder); } if (element instanceof HaxeThisExpression thisExpression) { @@ -320,7 +324,7 @@ static ResultHolder _handle(final PsiElement element, return handleVarInit(context, resolver, varInit); } - if(element instanceof HaxeObjectLiteralElement objectLiteralElement) { + if(element instanceof HaxeObjectLiteralElement objectLiteralElement && objectLiteralElement.getExpression() != null) { return handle(objectLiteralElement.getExpression(), context, resolver); } @@ -428,6 +432,31 @@ static ResultHolder _handle(final PsiElement element, return createUnknown(element); } + private static @Nullable ResultHolder findMissingTypeParametersIfNecessary(HaxeExpressionEvaluatorContext context, + HaxeGenericResolver resolver, + HaxeNewExpression expression, + ResultHolder typeHolder) { + // if new expression is missing typeParameters try to resolve from usage + HaxeType type = expression.getType(); + if (type.getTypeParam() == null && typeHolder.getClassType() != null && typeHolder.getClassType().getSpecifics().length > 0) { + HaxePsiField fieldDeclaration = PsiTreeUtil.getParentOfType(expression, HaxePsiField.class); + if (fieldDeclaration != null && fieldDeclaration.getTypeTag() == null) { + SpecificHaxeClassReference classType = typeHolder.getClassType(); + // if class does not have any generics there no need to search for references + if (classType != null && classType.getSpecifics().length > 0) { + HaxeComponentName componentName = fieldDeclaration.getComponentName(); + if(componentName != null) { + ResultHolder searchResult = searchReferencesForTypeParameters(componentName, context, resolver, typeHolder); + if (!searchResult.isUnknown()) { + return searchResult; + } + } + } + } + } + return typeHolder; + } + private static ResultHolder handeTypeCheckExpr(PsiElement element, HaxeTypeCheckExpr expr, HaxeGenericResolver resolver) { if (expr.getTypeOrAnonymous() != null) { return HaxeTypeResolver.getTypeFromTypeOrAnonymous(expr.getTypeOrAnonymous(), resolver); @@ -535,24 +564,34 @@ public static ResultHolder searchReferencesForType(final HaxeComponentName compo ) { List references = referenceSearch(componentName, searchScopePsi); ResultHolder lastValue = null; - for (PsiReference reference : references) { - ResultHolder possibleType = checkSearchResult(context, resolver, reference, componentName, hint); - if(possibleType != null) { + int continueFrom = 0; + for (int i = 0, size = references.size(); i < size; i++) { + PsiReference reference = references.get(i); + ResultHolder possibleType = checkSearchResult(context, resolver, reference, componentName, hint, i == 0); + if (possibleType != null) { if (!possibleType.isUnknown()) { if (lastValue == null) { lastValue = possibleType; + continueFrom = i+1; } if (!lastValue.isDynamic()) { // monomorphs should only use the first real value found (right?) //NOTE: don't use unify here (will break function type from usage) if (!lastValue.isFunctionType()) break; boolean canAssign = lastValue.canAssign(possibleType); - if (canAssign) lastValue = possibleType; + if (canAssign) { + lastValue = possibleType; + continueFrom = i+1; + } } } } } if (lastValue != null && !lastValue.isUnknown()) { + if(lastValue.containsTypeParameters()) { + ResultHolder holder = searchReferencesForTypeParameters(componentName, context, resolver, lastValue, continueFrom); + if (!holder.isUnknown()) return holder; + } return lastValue; } @@ -581,15 +620,17 @@ public static List referenceSearch(final HaxeComponentName compone @Nullable private static ResultHolder checkSearchResult(HaxeExpressionEvaluatorContext context, HaxeGenericResolver resolver, PsiReference reference, HaxeComponentName originalComponent, - @Nullable ResultHolder hint) { + @Nullable ResultHolder hint, boolean firstReference) { if (originalComponent.getParent() == reference) return null; if (reference instanceof HaxeExpression expression) { if (expression.getParent() instanceof HaxeAssignExpression assignExpression) { HaxeExpression rightExpression = assignExpression.getRightExpression(); - ResultHolder result = handle(rightExpression, context, resolver); - if (!result.isUnknown()) { - return result; + if(rightExpression != null) { + ResultHolder result = handle(rightExpression, context, resolver); + if (!result.isUnknown()) { + return result; + } } HaxeExpression leftExpression = assignExpression.getLeftExpression(); if (leftExpression instanceof HaxeReferenceExpression referenceExpression) { @@ -676,7 +717,7 @@ private static ResultHolder checkSearchResult(HaxeExpressionEvaluatorContext con final HaxeReference leftReference = PsiTreeUtil.getChildOfType(callExpression.getExpression(), HaxeReference.class); if (leftReference == reference) { if (resolved instanceof HaxeMethod method ) { - HaxeCallExpressionUtil.CallExpressionValidation validation = HaxeCallExpressionUtil.checkMethodCall(callExpression, method); + HaxeCallExpressionUtil.CallExpressionValidation validation = HaxeCallExpressionUtil.checkMethodCall(callExpression, method, firstReference); ResultHolder hintResolved = validation.getResolver().resolve(hint); if (hintResolved != null && hintResolved.getType() != hintResolved.getType()) return hintResolved; } diff --git a/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorCacheService.java b/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorCacheService.java index 7027e8398..90f33a6bd 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorCacheService.java +++ b/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorCacheService.java @@ -1,7 +1,10 @@ package com.intellij.plugins.haxe.model.evaluator; +import com.intellij.openapi.util.RecursionGuard; +import com.intellij.openapi.util.RecursionManager; import com.intellij.plugins.haxe.model.type.HaxeGenericResolver; import com.intellij.plugins.haxe.model.type.ResultHolder; +import com.intellij.plugins.haxe.model.type.SpecificTypeReference; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -19,13 +22,18 @@ public class HaxeExpressionEvaluatorCacheService { private volatile Map cacheMap = new ConcurrentHashMap<>(); - public boolean skipCaching = false; + public static boolean skipCaching = false;// just convenience flag for debugging - public @NotNull ResultHolder handleWithResultCaching(final PsiElement element, + + public @NotNull ResultHolder handleWithResultCaching(@NotNull final PsiElement element, final HaxeExpressionEvaluatorContext context, final HaxeGenericResolver resolver) { - if(skipCaching) return _handle(element, context, resolver); + if(skipCaching){ + ResultHolder holder = _handle(element, context, resolver); + if(holder == null) return SpecificTypeReference.getUnknown(element).createHolder(); + return holder; + } EvaluationKey key = new EvaluationKey(element, resolver == null ? "NO_RESOLVER" : resolver.toCacheString()); if (cacheMap.containsKey(key)) { @@ -33,7 +41,8 @@ public class HaxeExpressionEvaluatorCacheService { } else { ResultHolder holder = _handle(element, context, resolver); - if (!holder.isUnknown()) { + if(holder == null) return SpecificTypeReference.getUnknown(element).createHolder(); + if (!holder.isUnknown() && !holder.containsUnknownTypeParameters()) { cacheMap.put(key, holder); } return holder; diff --git a/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorHandlers.java b/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorHandlers.java index 42e6de51f..36a703d7d 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorHandlers.java +++ b/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionEvaluatorHandlers.java @@ -57,9 +57,9 @@ public class HaxeExpressionEvaluatorHandlers { static ResultHolder handleWithRecursionGuard(PsiElement element, HaxeExpressionEvaluatorContext context, HaxeGenericResolver resolver) { - RecursionManager.markStack(); + if (element == null ) return null; - return evaluatorHandlersRecursionGuard.doPreventingRecursion(element, false, () -> handle(element, context, resolver)); + return evaluatorHandlersRecursionGuard.doPreventingRecursion(element, true, () -> handle(element, context, resolver)); } @@ -158,10 +158,11 @@ static ResultHolder handleGuard( return expr.createHolder(); } + @Nullable static ResultHolder handleReferenceExpression( HaxeExpressionEvaluatorContext context, HaxeGenericResolver resolver, HaxeReferenceExpression element) { PsiElement[] children = element.getChildren(); - ResultHolder typeHolder; + ResultHolder typeHolder = null; if (children.length == 0) { typeHolder = SpecificTypeReference.getUnknown(element).createHolder(); }else { @@ -352,11 +353,7 @@ else if (subelement instanceof HaxeSwitchCaseExpr caseExpr) { else { // attempt to resolve sub-element using default handle logic if (!(subelement instanceof PsiPackage)) { - typeHolder = handleWithRecursionGuard(subelement, context, resolver); - - } - if (typeHolder == null) { - typeHolder = SpecificTypeReference.getUnknown(element).createHolder(); + typeHolder = handleWithRecursionGuard(subelement, context, resolver);//TODO null should probably block result cache } } } @@ -379,7 +376,8 @@ else if (subelement instanceof HaxeSwitchCaseExpr caseExpr) { return typeHolder; } - return SpecificTypeReference.getDynamic(element).createHolder(); + return typeHolder; + //return SpecificTypeReference.getDynamic(element).createHolder(); } private static boolean isReificationExpression(HaxeReferenceExpression element) { @@ -595,7 +593,7 @@ static ResultHolder handleNewExpression( HaxeMethod method = constructor.getMethod(); HaxeMethodModel methodModel = method.getModel(); if (methodModel.getGenericParams().isEmpty()) { - //TODO needs stackoverflow protecting + //TODO needs stackoverflow protecting ? HaxeCallExpressionUtil.CallExpressionValidation validation = HaxeCallExpressionUtil.checkConstructor(expression); HaxeGenericResolver resolverFromCallExpression = validation.getResolver(); @@ -607,20 +605,7 @@ static ResultHolder handleNewExpression( } } } - // if new expression is missing typeParameters try to resolve from usage - if (type.getTypeParam() == null && typeHolder.getClassType() != null && typeHolder.getClassType().getSpecifics().length > 0) { - HaxePsiField fieldDeclaration = PsiTreeUtil.getParentOfType(expression, HaxePsiField.class); - if (fieldDeclaration != null && fieldDeclaration.getTypeTag() == null) { - SpecificHaxeClassReference classType = typeHolder.getClassType(); - // if class does not have any generics there no need to search for references - if (classType != null && classType.getSpecifics().length > 0) { - ResultHolder searchResult = searchReferencesForTypeParameters(fieldDeclaration, context, resolver, typeHolder); - if (!searchResult.isUnknown()) { - typeHolder = searchResult; - } - } - } - } + if (typeHolder.getType() instanceof SpecificHaxeClassReference classReference) { final HaxeClassModel clazz = classReference.getHaxeClassModel(); if (clazz != null) { @@ -1178,8 +1163,11 @@ static ResultHolder handleArrayLiteral( HaxePsiField declaringField = UsefulPsiTreeUtil.findParentOfTypeButStopIfTypeIs(arrayLiteral, HaxePsiField.class, HaxeCallExpression.class); if (declaringField != null) { - ResultHolder searchResult = searchReferencesForTypeParameters(declaringField, context, resolver, holder); - if (!searchResult.isUnknown()) holder = searchResult; + HaxeComponentName componentName = declaringField.getComponentName(); + if(componentName != null) { + ResultHolder searchResult = searchReferencesForTypeParameters(componentName, context, resolver, holder); + if (!searchResult.isUnknown()) holder = searchResult; + } } } } @@ -1440,6 +1428,7 @@ private static boolean isCallExpressionToMacroMethod(HaxeExpression callExpressi } + @Nullable static ResultHolder handleVarInit( HaxeExpressionEvaluatorContext context, HaxeGenericResolver resolver, @@ -1448,11 +1437,11 @@ static ResultHolder handleVarInit( if (expression == null) { return SpecificTypeReference.getInvalid(varInit).createHolder(); } - ResultHolder holder = handleWithRecursionGuard(expression, context, resolver); - return holder != null ? holder : createUnknown(varInit); + return handleWithRecursionGuard(expression, context, resolver); + } - @NotNull + @Nullable static ResultHolder handleLocalVarDeclaration( HaxeExpressionEvaluatorContext context, HaxeGenericResolver resolver, @@ -1478,24 +1467,24 @@ static ResultHolder handleLocalVarDeclaration( } if (result == null && init != null) { - result = handle(init, context, localResolver); + result = _handle(init, context, localResolver); } - if (result == null || isDynamicBecauseOfNullValueInit(result) || isUnknownLiteralArray(result)) { - // search for usage to determine type - HaxeComponentName element = varDeclaration.getComponentName(); - final ResultHolder hint = result; + // search for usage to determine type + HaxeComponentName element = varDeclaration.getComponentName(); + final ResultHolder hint = result; + if (result == null || isDynamicBecauseOfNullValueInit(result)) { result = tryToFindTypeFromUsage(element, result, hint, context, resolver, null); + } - if(result != null && result.containsUnknownTypeParameters()) { - result = searchReferencesForTypeParameters(varDeclaration, context, resolver, result); - } - + if (isUnknownLiteralArray(result) && result.containsUnknownTypeParameters()) { + result = searchReferencesForTypeParameters(name, context, resolver, result); } + result = tryGetEnumValuesDeclaringClass(result); context.setLocal(name.getText(), result); - return result != null ? result : createUnknown(varDeclaration); + return result; } @@ -1508,6 +1497,7 @@ static ResultHolder handleLocalVarDeclaration( } private static boolean isUnknownLiteralArray(ResultHolder result) { + if (result == null) return false; SpecificHaxeClassReference classType = result.getClassType(); if (classType != null && result.getClassType().isArray()) { @NotNull ResultHolder[] specifics = classType.getSpecifics(); @@ -1523,6 +1513,7 @@ private static boolean isUnknownLiteralArray(ResultHolder result) { } public static boolean isDynamicBecauseOfNullValueInit(ResultHolder result) { + if (result == null) return false; return result.getType().isDynamic() && result.getType().getConstant() instanceof HaxeNull; } diff --git a/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionUsageUtil.java b/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionUsageUtil.java index 2a8bc97fe..35b6c833e 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionUsageUtil.java +++ b/src/main/java/com/intellij/plugins/haxe/model/evaluator/HaxeExpressionUsageUtil.java @@ -82,11 +82,16 @@ public class HaxeExpressionUsageUtil { @NotNull - public static ResultHolder searchReferencesForTypeParameters(final HaxePsiField field, + public static ResultHolder searchReferencesForTypeParameters(final HaxeComponentName componentName, final HaxeExpressionEvaluatorContext context, final HaxeGenericResolver resolver, ResultHolder resultHolder) { + return searchReferencesForTypeParameters(componentName,context,resolver,resultHolder,0); + } + @NotNull + public static ResultHolder searchReferencesForTypeParameters(final HaxeComponentName componentName, + final HaxeExpressionEvaluatorContext context, + final HaxeGenericResolver resolver, ResultHolder resultHolder, int continueFrom) { resultHolder = resultHolder.duplicate(); - HaxeComponentName componentName = field.getComponentName(); SpecificHaxeClassReference type = resultHolder.getClassType(); SpecificHaxeClassReference classType = type; @@ -97,12 +102,14 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxePsiField List references = referenceSearch(componentName, useScope); //TODO we should handle this logic as monomorph as well and only accpet first value // NOTE : we might have to change this code a bit if we need to iterate several references to collect multiple typeParameters / specifics - for (PsiReference reference : references) { + boolean isFirst = true; + for (int i = continueFrom, size = references.size(); i < size; i++) { + PsiReference reference = references.get(i); if (reference instanceof HaxeExpression expression) { PsiElement parent = expression.getParent(); if (reference instanceof HaxeReferenceExpression referenceExpression) { - ResultHolder unified = tryTypeFromParameterFromCallExpression(resultHolder, referenceExpression, parent); + ResultHolder unified = tryFindTypeWhenUsedAsParameterInCallExpression(resultHolder, referenceExpression, parent); if (unified != null) return unified; } @@ -111,7 +118,7 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxePsiField if (result != null) return result; } if (parent instanceof HaxeReferenceExpression referenceExpression) { - ResultHolder resolve = tryTypeFromReference(resultHolder, referenceExpression); + ResultHolder resolve = tryFindTypeFromMethodCallOnReference(resultHolder, referenceExpression, isFirst); if (resolve != null) return resolve; } @@ -128,6 +135,7 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxePsiField tryUpdateTypePAramFromOjbectLiteral(context, resolver, literalElement, type); } } + isFirst = false; } return resultHolder; } @@ -282,7 +290,7 @@ private static void tryUpdateTypeParamFromArrayAccess(HaxeExpressionEvaluatorCon return null; } - private static @Nullable ResultHolder tryTypeFromReference(ResultHolder resultHolder, HaxeReferenceExpression referenceExpression) { + private static @Nullable ResultHolder tryFindTypeFromMethodCallOnReference(ResultHolder resultHolder, HaxeReferenceExpression referenceExpression, boolean isFirstRef) { PsiElement resolved = referenceExpression.resolve(); if (resolved instanceof HaxeMethodDeclaration methodDeclaration && referenceExpression.getParent() instanceof HaxeCallExpression callExpression) { @@ -290,7 +298,7 @@ private static void tryUpdateTypeParamFromArrayAccess(HaxeExpressionEvaluatorCon HaxeMethodModel methodModel = methodDeclaration.getModel(); HaxeCallExpressionUtil.CallExpressionValidation validation = - HaxeCallExpressionUtil.checkMethodCall(callExpression, methodModel.getMethod()); + HaxeCallExpressionUtil.checkMethodCall(callExpression, methodModel.getMethod(), isFirstRef); HaxeGenericResolver resolverFromCallExpression = validation.getResolver(); if (resolverFromCallExpression != null) { @@ -323,9 +331,9 @@ private static void tryUpdateTypeParamFromArrayAccess(HaxeExpressionEvaluatorCon return null; } - private static @Nullable ResultHolder tryTypeFromParameterFromCallExpression(ResultHolder resultHolder, - HaxeReferenceExpression referenceExpression, - PsiElement parent) { + private static @Nullable ResultHolder tryFindTypeWhenUsedAsParameterInCallExpression(ResultHolder resultHolder, + HaxeReferenceExpression referenceExpression, + PsiElement parent) { if (parent != null && parent.getParent() instanceof HaxeCallExpression callExpression) { if (callExpression.getExpression() instanceof HaxeReference callExpressionReference) { final PsiElement resolved = callExpressionReference.resolve(); diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/ResultHolder.java b/src/main/java/com/intellij/plugins/haxe/model/type/ResultHolder.java index ec55a9ea1..f10b26567 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/ResultHolder.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/ResultHolder.java @@ -227,7 +227,7 @@ public static boolean containsUnknownTypeParameters(ResultHolder holder) { SpecificTypeReference type = holder.getType(); if (type instanceof SpecificHaxeClassReference classReference) { for (ResultHolder specific : classReference.getSpecifics()) { - if (type.isUnknown() || containsUnknownTypeParameters(specific)) return true; + if (specific.isUnknown() || containsUnknownTypeParameters(specific)) return true; } } if (type instanceof SpecificFunctionReference function) { diff --git a/src/test/java/com/intellij/plugins/haxe/ide/HaxeSemanticAnnotatorTest.java b/src/test/java/com/intellij/plugins/haxe/ide/HaxeSemanticAnnotatorTest.java index 4100a30eb..749c663c5 100644 --- a/src/test/java/com/intellij/plugins/haxe/ide/HaxeSemanticAnnotatorTest.java +++ b/src/test/java/com/intellij/plugins/haxe/ide/HaxeSemanticAnnotatorTest.java @@ -639,6 +639,11 @@ public void testNoErrorOnConstrainedGenericOverrides() throws Exception { public void testMissingInterfaceMethodsOnConstrainedGenericOverrides() throws Exception { doTestNoFixWithWarnings(); } + @Test + public void testMonomorphism() throws Exception { + doTestNoFixWithWarnings(); + } + @Test public void testMultiLevelGenericInheritance() throws Exception { doTestNoFixWithWarnings(); diff --git a/src/test/resources/testData/annotation.semantic/Monomorphism.hx b/src/test/resources/testData/annotation.semantic/Monomorphism.hx new file mode 100644 index 000000000..d48bdbaf9 --- /dev/null +++ b/src/test/resources/testData/annotation.semantic/Monomorphism.hx @@ -0,0 +1,53 @@ +class MonomorphTest { + + public function basicMonomorphism():Void { + var normal; + normal = ""; + // Wrong already morphed to String + normal = 1; + + var normalNulled = null; + normalNulled = ""; + // Wrong already morphed to String + normalNulled = 1; + + var arrayInit = []; + arrayInit.push("Test"); + // Wrong already morphed to Array + arrayInit.push(1); + + + var mapInit = new Map(); + mapInit.set("test", 1); + // Wrong already morphed to Map + mapInit.set(1, "string"); + + var arrayDelayed; + arrayDelayed = []; + arrayDelayed.push("Test"); + // Wrong already morphed to Array + arrayDelayed.push(1); + + + var mapDelayed; + mapDelayed = new Map(); + mapDelayed.set("test", 1); + // Wrong already morphed to Map + mapDelayed.set(1, "test"); + } + + public function advancemonomorphism(morphA, morphB):Void { + var arr = [""]; + arr.push(morphA); + + morphA = 1;// Wrong already morphed String + var test1:Int = morphA; // Wrong already morphed String + + + var obj:{a:String, b:Int} = {a:morphA, b:morphB}; + + morphB = ""; // Wrong already morphed Int + var test2:String = morphB; // Wrong already morphed Int + } + +}