From 37ec417a3e180e9717b64b5c2a2afc666d577343 Mon Sep 17 00:00:00 2001 From: m0rkeulv Date: Fri, 26 Apr 2024 00:57:50 +0200 Subject: [PATCH] reworked how openParameterlist is handled to help figure out the type for the untyped parameter (should fix issue #1165) --- .../semantics/HaxeCallExpressionUtil.java | 7 ++++ .../plugins/haxe/lang/parser/haxe.bnf | 5 ++- .../plugins/haxe/lang/psi/HaxeResolver.java | 4 +-- .../haxe/lang/psi/impl/HaxeReferenceImpl.java | 32 ++++++++++++++----- .../type/HaxeExpressionEvaluatorHandlers.java | 22 +++++++------ .../UntypedWithGenerics.hx | 3 +- .../ArrowFnSingleArgBareAssignment.txt | 7 ++-- .../function/ArrowFnSingleArgBareNested.txt | 7 ++-- 8 files changed, 59 insertions(+), 28 deletions(-) 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 d80723206..52e6d847c 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 @@ -192,6 +192,13 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi // attempt re-resolve after adding inherited type parameters parameterType = resolveParameterType(parameter, argumentResolver); } + // heck if functionType has untyped open parameterlist, if so inherit type + if (parameterType.isFunctionType() && argumentType.isFunctionType() + && argument instanceof HaxeFunctionLiteral literal && literal.getOpenParameterList() != null) { + SpecificFunctionReference paramFn = parameterType.getFunctionType(); + SpecificFunctionReference argFn = argumentType.getFunctionType(); + argumentType = new SpecificFunctionReference(paramFn.getArguments(), argFn.getReturnType(), null, literal, literal).createHolder(); + } //TODO properly resolve typedefs SpecificHaxeClassReference argumentClass = argumentType.getClassType(); diff --git a/src/main/java/com/intellij/plugins/haxe/lang/parser/haxe.bnf b/src/main/java/com/intellij/plugins/haxe/lang/parser/haxe.bnf index d3e591424..83651b93a 100644 --- a/src/main/java/com/intellij/plugins/haxe/lang/parser/haxe.bnf +++ b/src/main/java/com/intellij/plugins/haxe/lang/parser/haxe.bnf @@ -465,7 +465,10 @@ restParameter ::= ('...'componentName typeTag?) // An open parameter can't have all of the type and init info. Plus, it makes the parser *very* *very* slow in some circumstances. -openParameterList ::= componentName {extends=parameterList} +untypedParameter ::= componentName {extends=parameter} +openParameterList ::= untypedParameter +{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeParameterListPsiMixinImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeParameterListPsiMixin"} + private parenthesizedParameterList ::= '(' parameterList ')' parameterList ::= (parameter ((',' parameter)* (',' restParameter)?)? | restParameter)? {recoverWhile="parameterListRecovery" mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeParameterListPsiMixinImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeParameterListPsiMixin"} private parameterListRecovery ::= !('(' | ')') // Open parens are allowable in arrow function arguments. diff --git a/src/main/java/com/intellij/plugins/haxe/lang/psi/HaxeResolver.java b/src/main/java/com/intellij/plugins/haxe/lang/psi/HaxeResolver.java index badf65e4c..aa403e53a 100644 --- a/src/main/java/com/intellij/plugins/haxe/lang/psi/HaxeResolver.java +++ b/src/main/java/com/intellij/plugins/haxe/lang/psi/HaxeResolver.java @@ -1490,8 +1490,8 @@ public boolean execute(@NotNull PsiElement element, ResolveState state) { else if (element instanceof HaxeNamedComponent) { componentName = ((HaxeNamedComponent)element).getComponentName(); } - else if (element instanceof HaxeOpenParameterList) { - componentName = ((HaxeOpenParameterList)element).getComponentName(); + else if (element instanceof HaxeOpenParameterList parameterList) { + componentName = parameterList.getUntypedParameter().getComponentName(); } else if (element instanceof HaxeSwitchCaseExpr expr) { if (!executeForSwitchCase(expr)) return false; diff --git a/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java b/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java index b8b2b08fd..d2b33dd8d 100644 --- a/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java +++ b/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java @@ -978,20 +978,36 @@ public static ResultHolder tryToFindTypeFromCallExpression(@NotNull HaxeFunction ResultHolder type = model.getType(); if (type.isFunctionType()) { SpecificFunctionReference functionType = type.getFunctionType(); - HaxeParameterList parameterList = literal.getParameterList(); - if (parameterList == null || functionType == null) return null; - int parameterMappedToArgument = parameterList.getParameterList().indexOf(parameter); - List arguments = functionType.getArguments(); - SpecificFunctionReference.Argument argument = arguments.get(parameterMappedToArgument); - - ResultHolder resolved = validation.getResolver().withoutUnknowns().resolve(argument.getType()); - if (resolved != null && !resolved.isUnknown()) return resolved.getType().createHolder(); + if (functionType == null) return null; + int index = findParameterIndex(literal, parameter); + if (index > -1 ) { + List arguments = functionType.getArguments(); + SpecificFunctionReference.Argument argument = arguments.get(index); + + ResultHolder resolved = validation.getResolver().withoutUnknowns().resolve(argument.getType()); + if (resolved != null && !resolved.isUnknown()) return resolved.getType().createHolder(); + } } } return null; }); } + private static int findParameterIndex(@NotNull HaxeFunctionLiteral literal, @NotNull PsiElement parameter) { + HaxeParameterList parameterList = literal.getParameterList(); + if (parameterList != null) { + return parameterList.getParameterList().indexOf(parameter); + } + + HaxeOpenParameterList openParameterList = literal.getOpenParameterList(); + if (openParameterList != null) { + if(openParameterList.getUntypedParameter() == parameter) { + return 0; + } + } + return -1; + } + private static final RecursionGuard genericResolverRecursionGuard = RecursionManager.createGuard("genericResolverRecursionGuard"); 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 04c088973..448c6e02e 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 @@ -348,7 +348,8 @@ else if (subelement instanceof HaxeSwitchCaseExpr caseExpr) { if (subelement instanceof HaxeImportAlias) return typeHolder; // overriding context to avoid problems with canAssign thinking this is a "Pure" class reference - return typeHolder.withElementContext(element); + if (!typeHolder.isFunctionType()) return typeHolder.withElementContext(element); + return typeHolder; } return SpecificTypeReference.getDynamic(element).createHolder(); @@ -734,25 +735,26 @@ static ResultHolder handleFunctionLiteral( HaxeExpressionEvaluatorContext context, HaxeGenericResolver resolver, HaxeFunctionLiteral function) { - HaxeParameterList params = function.getParameterList(); // TODO mlo: get expected type to use if signature/parameters are without types - if (params == null) { - return SpecificHaxeClassReference.getInvalid(function).createHolder(); - } + //HaxeParameterList params = function.getParameterList(); // TODO mlo: get expected type to use if signature/parameters are without types + //if (params == null) { + // return SpecificHaxeClassReference.getInvalid(function).createHolder(); + //} LinkedList arguments = new LinkedList<>(); ResultHolder returnType = null; context.beginScope(); try { - if (params instanceof HaxeOpenParameterList openParamList) { + HaxeOpenParameterList openParamList = function.getOpenParameterList(); + HaxeParameterList parameterList = function.getParameterList(); + if (openParamList != null) { // Arrow function with a single, unparenthesized, parameter. - // TODO: Infer the type from first usage in the function body. ResultHolder argumentType = SpecificTypeReference.getUnknown(function).createHolder(); - String argumentName = openParamList.getComponentName().getName(); + String argumentName = openParamList.getUntypedParameter().getComponentName().getName(); context.setLocal(argumentName, argumentType); // TODO check if rest param? arguments.add(new SpecificFunctionReference.Argument(0, false, false, argumentType, argumentName)); - } else { - List list = params.getParameterList(); + } else if (parameterList != null) { + List list = parameterList.getParameterList(); for (int i = 0; i < list.size(); i++) { HaxeParameter parameter = list.get(i); //ResultHolder argumentType = HaxeTypeResolver.getTypeFromTypeTag(parameter.getTypeTag(), function); diff --git a/src/test/resources/testData/inlay/haxe.untyped.parameter.type/UntypedWithGenerics.hx b/src/test/resources/testData/inlay/haxe.untyped.parameter.type/UntypedWithGenerics.hx index 2f3abc641..82dc00e45 100644 --- a/src/test/resources/testData/inlay/haxe.untyped.parameter.type/UntypedWithGenerics.hx +++ b/src/test/resources/testData/inlay/haxe.untyped.parameter.type/UntypedWithGenerics.hx @@ -5,7 +5,8 @@ class Untypedgenerics { // function literal myArr.filter(function(arg/*<# :String #>*/) {return true;}); // lambda - myArr.filter((arg/*<# :String #>*/) -> true); + myArr.filter((arg/*<# :String #>*/) -> arg.length > 0); + myArr.filter(arg/*<# :String #>*/ -> arg.length < 0); myArr.sort((s1/*<# :String #>*/,s2/*<# :String #>*/)-> -1); testMethodGenericPassing("", (arg/*<# :String #>*/)-> {}); diff --git a/src/test/resources/testData/parsing/haxe/declarations/function/ArrowFnSingleArgBareAssignment.txt b/src/test/resources/testData/parsing/haxe/declarations/function/ArrowFnSingleArgBareAssignment.txt index 809108523..e2b01752a 100644 --- a/src/test/resources/testData/parsing/haxe/declarations/function/ArrowFnSingleArgBareAssignment.txt +++ b/src/test/resources/testData/parsing/haxe/declarations/function/ArrowFnSingleArgBareAssignment.txt @@ -62,9 +62,10 @@ Haxe File HaxePsiToken:=('=') FUNCTION_LITERAL OPEN_PARAMETER_LIST - COMPONENT_NAME - IDENTIFIER - HaxePsiToken:ID('a') + UNTYPED_PARAMETER + COMPONENT_NAME + IDENTIFIER + HaxePsiToken:ID('a') HaxePsiToken:->('->') CALL_EXPRESSION REFERENCE_EXPRESSION diff --git a/src/test/resources/testData/parsing/haxe/declarations/function/ArrowFnSingleArgBareNested.txt b/src/test/resources/testData/parsing/haxe/declarations/function/ArrowFnSingleArgBareNested.txt index f418d2d11..dfa0684c8 100644 --- a/src/test/resources/testData/parsing/haxe/declarations/function/ArrowFnSingleArgBareNested.txt +++ b/src/test/resources/testData/parsing/haxe/declarations/function/ArrowFnSingleArgBareNested.txt @@ -96,9 +96,10 @@ Haxe File CALL_EXPRESSION_LIST FUNCTION_LITERAL OPEN_PARAMETER_LIST - COMPONENT_NAME - IDENTIFIER - HaxePsiToken:ID('a') + UNTYPED_PARAMETER + COMPONENT_NAME + IDENTIFIER + HaxePsiToken:ID('a') HaxePsiToken:->('->') CALL_EXPRESSION REFERENCE_EXPRESSION