From bbc711c1c84f0899edb45eeb63221ea4cf3cad59 Mon Sep 17 00:00:00 2001 From: jlerbsc Date: Sat, 26 Aug 2023 18:13:28 +0200 Subject: [PATCH 1/2] Fix: issue 3976 Issue resolving implicit generic types --- .../contexts/MethodCallExprContext.java | 74 +++++++++++++++++++ .../symbolsolver/Issue3976Test.java | 33 +++++++++ 2 files changed, 107 insertions(+) create mode 100755 javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/Issue3976Test.java diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java index 08d8266856..21b0a608c6 100644 --- a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java @@ -22,6 +22,7 @@ package com.github.javaparser.symbolsolver.javaparsermodel.contexts; import java.util.*; +import java.util.stream.Collectors; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; @@ -38,6 +39,7 @@ import com.github.javaparser.resolution.model.typesystem.ReferenceTypeImpl; import com.github.javaparser.resolution.types.*; import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; +import com.github.javaparser.symbolsolver.resolution.typeinference.LeastUpperBoundLogic; import com.github.javaparser.utils.Pair; public class MethodCallExprContext extends AbstractJavaParserContext { @@ -408,14 +410,86 @@ private MethodUsage resolveMethodTypeParameters(MethodUsage methodUsage, List matchedTypeParameters) { + // first group all resolved types by type variable + Map> resolvedTypesByTypeVariable = groupResolvedTypeByTypeVariable(matchedTypeParameters); + // then reduce the list of resolved types with the least upper bound logic + Map reducedResolvedTypesByTypeVariable = reduceResolvedTypesByTypeVariable(resolvedTypesByTypeVariable); + // then replace resolved type by the reduced type for each type variable + convertTypesParameters(matchedTypeParameters, reducedResolvedTypesByTypeVariable); + // finally replace type parameters for (ResolvedTypeParameterDeclaration tp : matchedTypeParameters.keySet()) { methodUsage = methodUsage.replaceTypeParameter(tp, matchedTypeParameters.get(tp)); } return methodUsage; } + /* + * Update the matchedTypeParameters map from the types in reducedResolvedTypesByTypeVariable map. + */ + private void convertTypesParameters( + Map matchedTypeParameters, + Map reducedResolvedTypesByTypeVariable) { + for (ResolvedTypeParameterDeclaration tp : matchedTypeParameters.keySet()) { + String typeParameterName = tp.getName(); + boolean replacement = reducedResolvedTypesByTypeVariable.keySet().contains(typeParameterName); + if (replacement) { + matchedTypeParameters.put(tp, reducedResolvedTypesByTypeVariable.get(typeParameterName)); + } + } + } + + /* + * Group resolved type by the variable type. For example in Map.of("k0", 0, "k1", + * 1D) which is solved as static Map of(K k1, V v1, K k2, V v2) + * the type variable named V that represents the type of the first and fourth parameter + * must reference v1 (Integer type) and v2 (Double type). + */ + private Map> groupResolvedTypeByTypeVariable(Map typeParameters) { + Map> resolvedTypesByTypeVariable = new HashMap<>(); + for (ResolvedTypeParameterDeclaration tp : typeParameters.keySet()) { + String typeParameterName = tp.getName(); + boolean alreadyCollected = resolvedTypesByTypeVariable.keySet().contains(typeParameterName); + if (!alreadyCollected) { + Set resolvedTypes = findResolvedTypesByTypeVariable(typeParameterName, typeParameters); + resolvedTypesByTypeVariable.put(typeParameterName, resolvedTypes); + } + } + return resolvedTypesByTypeVariable; + } + + /* + * Collect all resolved type from a type variable name + */ + private Set findResolvedTypesByTypeVariable(String typeVariableName, Map typeParameters) { + return typeParameters.keySet().stream() + .filter(resolvedTypeParameterDeclaration -> resolvedTypeParameterDeclaration.getName().equals(typeVariableName)) + .map(resolvedTypeParameterDeclaration -> typeParameters.get(resolvedTypeParameterDeclaration)) + .collect(Collectors.toSet()); + } + + /* + * Reduce all set of resolved type with LUB + */ + private Map reduceResolvedTypesByTypeVariable(Map> typeParameters) { + Map reducedResolvedTypesList = new HashMap<>(); + for (String typeParameterName : typeParameters.keySet()) { + ResolvedType type = reduceResolvedTypesWithLub(typeParameters.get(typeParameterName)); + reducedResolvedTypesList.put(typeParameterName, type); + } + return reducedResolvedTypesList; + } + + private ResolvedType reduceResolvedTypesWithLub(Set resolvedTypes) { + return LeastUpperBoundLogic.of().lub(resolvedTypes); + } + private void matchTypeParameters(ResolvedType expectedType, ResolvedType actualType, Map matchedTypeParameters) { if (expectedType.isTypeVariable()) { ResolvedType type = actualType; diff --git a/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/Issue3976Test.java b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/Issue3976Test.java new file mode 100755 index 0000000000..be3833f340 --- /dev/null +++ b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/Issue3976Test.java @@ -0,0 +1,33 @@ +package com.github.javaparser.symbolsolver; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import com.github.javaparser.JavaParserAdapter; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.resolution.types.ResolvedType; +import com.github.javaparser.symbolsolver.resolution.AbstractResolutionTest; + +public class Issue3976Test extends AbstractResolutionTest { + + @Test + void test() { + String testCase = + "import java.util.Map;\n" + + "public class Foo{\n" + + " public Object m() {\n" + + " return Map.of(\"k0\", 0, \"k1\", 1D);\n" + + " }\n" + + "}\n" + + ""; + + CompilationUnit cu = JavaParserAdapter.of(createParserWithResolver(defaultTypeSolver())).parse(testCase); + + MethodCallExpr methodCallExpr = cu.findFirst(MethodCallExpr.class).get(); + + ResolvedType rt = methodCallExpr.calculateResolvedType(); + assertEquals("java.util.Map", rt.describe()); + } +} From b4b7eb20e0001e2303744fa088094b9357a2f1d0 Mon Sep 17 00:00:00 2001 From: jlerbsc Date: Sat, 26 Aug 2023 19:49:36 +0200 Subject: [PATCH 2/2] Test method is only enabled for a specific range of Java RuntimeEnvironment (JRE) versions from java 9 --- .../javaparser/symbolsolver/Issue3976Test.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/Issue3976Test.java b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/Issue3976Test.java index be3833f340..899adec21e 100755 --- a/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/Issue3976Test.java +++ b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/Issue3976Test.java @@ -3,6 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import com.github.javaparser.JavaParserAdapter; import com.github.javaparser.ast.CompilationUnit; @@ -13,15 +15,15 @@ public class Issue3976Test extends AbstractResolutionTest { @Test + @EnabledForJreRange(min = JRE.JAVA_9) void test() { String testCase = "import java.util.Map;\n" - + "public class Foo{\n" - + " public Object m() {\n" - + " return Map.of(\"k0\", 0, \"k1\", 1D);\n" - + " }\n" - + "}\n" - + ""; + + "public class Foo {\n" + + " public Object m() {\n" + + " return Map.of(\"k0\", 0, \"k1\", 1D);\n" + + " }\n" + + "}"; CompilationUnit cu = JavaParserAdapter.of(createParserWithResolver(defaultTypeSolver())).parse(testCase);