diff --git a/src/main/java/ca/concordia/jaranalyzer/util/InferenceUtility.java b/src/main/java/ca/concordia/jaranalyzer/util/InferenceUtility.java index b089dd3..61b6d58 100644 --- a/src/main/java/ca/concordia/jaranalyzer/util/InferenceUtility.java +++ b/src/main/java/ca/concordia/jaranalyzer/util/InferenceUtility.java @@ -75,6 +75,8 @@ public static List getEligibleMethodInfoList(Set dependent importStatementList, variableNameMap, expression, owningClassInfo); } + TypeInfo returnTypeInfo = getReturnParameterizedTypeInfo(methodInvocation, variableNameMap); + TypeInferenceFluentAPI.Criteria searchCriteria = TypeInferenceFluentAPI.getInstance() .new Criteria(dependentArtifactSet, javaVersion, importStatementList, methodName, numberOfParameters) @@ -94,8 +96,8 @@ public static List getEligibleMethodInfoList(Set dependent * Order of transformTypeInfoRepresentation and conversionToVarargsMethodArgument matters. since we are * converting last argument array type to vararg. */ - transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, - owningClassInfo, methodInfoList, argumentTypeInfoList, typeArgumentTypeInfoList, invokerClassTypeInfo); + transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, owningClassInfo, + methodInfoList, argumentTypeInfoList, typeArgumentTypeInfoList, invokerClassTypeInfo, returnTypeInfo); conversionToVarargsMethodArgument(methodInfoList); return methodInfoList; @@ -139,7 +141,7 @@ public static List getEligibleMethodInfoList(Set dependent * converting last argument array type to vararg. */ transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, - owningClassInfo, methodInfoList, argumentTypeInfoList, typeArgumentTypeInfoList, null); + owningClassInfo, methodInfoList, argumentTypeInfoList, typeArgumentTypeInfoList, null, null); conversionToVarargsMethodArgument(methodInfoList); return methodInfoList; @@ -164,36 +166,10 @@ public static List getEligibleMethodInfoList(Set dependent importStatementList, typeArgumentList, owningClassInfo); Type type = classInstanceCreation.getType(); - TypeInfo invokerClassTypeInfo = null; + TypeInfo returnTypeInfo = null; if (type.isParameterizedType() && ((ParameterizedType) type).typeArguments().isEmpty()) { - VariableDeclarationStatement variableDeclarationStatement - = (VariableDeclarationStatement) InferenceUtility.getClosestASTNode(classInstanceCreation, VariableDeclarationStatement.class); - - if (Objects.nonNull(variableDeclarationStatement)) { - Type returnType = variableDeclarationStatement.getType(); - - if (returnType.isParameterizedType()) { - ParameterizedType parameterizedReturnType = (ParameterizedType) returnType; - - if (!parameterizedReturnType.typeArguments().isEmpty()) { - List returnTypeArgumentList = parameterizedReturnType.typeArguments(); - - List returnTypeInfoArgumentList = - getTypeInfoList(dependentArtifactSet, javaVersion, importStatementList, - returnTypeArgumentList, owningClassInfo); - - invokerClassTypeInfo = InferenceUtility.getTypeInfo(dependentArtifactSet, javaVersion, - importStatementList, type, owningClassInfo); - - assert invokerClassTypeInfo.isParameterizedTypeInfo(); - - ParameterizedTypeInfo parameterizedTypeInfo = (ParameterizedTypeInfo) invokerClassTypeInfo; - parameterizedTypeInfo.setParameterized(true); - parameterizedTypeInfo.setTypeArgumentList(returnTypeInfoArgumentList); - } - } - } + returnTypeInfo = getReturnParameterizedTypeInfo(classInstanceCreation, variableNameMap); } TypeInferenceFluentAPI.Criteria searchCriteria = TypeInferenceFluentAPI.getInstance() @@ -208,7 +184,7 @@ public static List getEligibleMethodInfoList(Set dependent List methodInfoList = searchCriteria.getMethodList(); InferenceUtility.transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, - owningClassInfo, methodInfoList, argumentTypeInfoList, typeArgumentTypeInfoList, invokerClassTypeInfo); + owningClassInfo, methodInfoList, argumentTypeInfoList, typeArgumentTypeInfoList, null, returnTypeInfo); conversionToVarargsMethodArgument(methodInfoList); return methodInfoList; @@ -258,8 +234,8 @@ public static List getEligibleMethodInfoList(Set dependent * Order of transformTypeInfoRepresentation and conversionToVarargsMethodArgument matters. since we are * converting last argument array type to vararg. */ - transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, - owningClassInfo, methodInfoList, Collections.emptyList(), typeArgumentTypeInfoList, invokerClassTypeInfo); + transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, owningClassInfo, + methodInfoList, Collections.emptyList(), typeArgumentTypeInfoList, invokerClassTypeInfo, null); conversionToVarargsMethodArgument(methodInfoList); return methodInfoList; @@ -299,8 +275,8 @@ public static List getEligibleMethodInfoList(Set dependent * Order of transformTypeInfoRepresentation and conversionToVarargsMethodArgument matters. since we are * converting last argument array type to vararg. */ - transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, - owningClassInfo, methodInfoList, Collections.emptyList(), typeArgumentTypeInfoList, null); + transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, owningClassInfo, + methodInfoList, Collections.emptyList(), typeArgumentTypeInfoList, null, null); conversionToVarargsMethodArgument(methodInfoList); return methodInfoList; @@ -341,8 +317,8 @@ public static List getEligibleMethodInfoList(Set dependent * Order of transformTypeInfoRepresentation and conversionToVarargsMethodArgument matters. since we are * converting last argument array type to vararg. */ - transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, - owningClassInfo, methodInfoList, Collections.emptyList(), typeArgumentTypeInfoList, null); + transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, owningClassInfo, + methodInfoList, Collections.emptyList(), typeArgumentTypeInfoList, null, null); conversionToVarargsMethodArgument(methodInfoList); return methodInfoList; @@ -386,8 +362,8 @@ public static List getEligibleMethodInfoList(Set dependent * Order of transformTypeInfoRepresentation and conversionToVarargsMethodArgument matters. since we are * converting last argument array type to vararg. */ - transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, - owningClassInfo, methodInfoList, Collections.emptyList(), typeArgumentTypeInfoList, invokerClassTypeInfo); + transformTypeInfoRepresentation(dependentArtifactSet, javaVersion, importStatementList, owningClassInfo, + methodInfoList, Collections.emptyList(), typeArgumentTypeInfoList, invokerClassTypeInfo, null); conversionToVarargsMethodArgument(methodInfoList); return methodInfoList; @@ -794,7 +770,7 @@ public static TypeInfo getTypeInfoFromExpression(Set dependentArtifact if (expression instanceof QualifiedName) { String firstPart = name.substring(0, name.indexOf(".")); - VariableDeclarationDto selected = getClassNameFromVariableMap(firstPart, expression, variableNameMap); + VariableDeclarationDto selected = getVariableDeclarationDtoFromVariableMap(firstPart, expression, variableNameMap); String className = selected != null ? selected.getTypeInfo().getQualifiedClassName() : null; if (className != null) { @@ -811,7 +787,7 @@ public static TypeInfo getTypeInfoFromExpression(Set dependentArtifact owningClassInfo); } } else if (expression instanceof SimpleName) { - VariableDeclarationDto selected = getClassNameFromVariableMap(name, expression, variableNameMap); + VariableDeclarationDto selected = getVariableDeclarationDtoFromVariableMap(name, expression, variableNameMap); TypeInfo classTypeInfo = selected != null ? selected.getTypeInfo() : null; if (Objects.nonNull(classTypeInfo)) { @@ -1198,7 +1174,8 @@ public static void transformTypeInfoRepresentation(Set dependentArtifa List methodInfoList, List argumentTypeInfoList, List typeArgumentTypeInfoList, - TypeInfo invokerTypeInfo) { + TypeInfo invokerTypeInfo, + TypeInfo returnTypeInfo) { for (MethodInfo methodInfo : methodInfoList) { convertParameterizedTypeIfRequired(dependentArtifactSet, javaVersion, importStatementList, @@ -1206,7 +1183,8 @@ public static void transformTypeInfoRepresentation(Set dependentArtifa Map replacedTypeInfoMap = new HashMap<>(); invokerTypeInfo = getOriginalInvoker(invokerTypeInfo, methodInfo, owningClassInfo); - Map formalTypeParameterMap = getInferredFormalTypeParameterMap(invokerTypeInfo, methodInfo, argumentTypeInfoList, typeArgumentTypeInfoList); + Map formalTypeParameterMap = getInferredFormalTypeParameterMap(invokerTypeInfo, + returnTypeInfo, methodInfo, argumentTypeInfoList, typeArgumentTypeInfoList); if (Objects.nonNull(owningClassInfo) && Objects.nonNull(invokerTypeInfo) && owningClassInfo.getQualifiedClassNameSetInHierarchy().get(0).contains(invokerTypeInfo.getQualifiedClassName())) { @@ -1272,6 +1250,31 @@ public static List getInnerClassQNameList(AbstractTypeDeclaration abstra } + private static TypeInfo getReturnParameterizedTypeInfo(ASTNode methodNode, + Map> variableNameMap) { + VariableDeclarationStatement variableDeclarationStatement + = (VariableDeclarationStatement) InferenceUtility.getClosestASTNode(methodNode, VariableDeclarationStatement.class); + + if (Objects.nonNull(variableDeclarationStatement)) { + List fragmentList = variableDeclarationStatement.fragments(); + + if (!fragmentList.isEmpty()) { + VariableDeclarationFragment fragment = fragmentList.get(0); + String variableName = fragment.getName().getFullyQualifiedName(); + + VariableDeclarationDto variableDeclarationDto = + getVariableDeclarationDtoFromVariableMap(variableName, fragment.getStartPosition(), variableNameMap); + + if (Objects.nonNull(variableDeclarationDto)) { + return variableDeclarationDto.getTypeInfo(); + } + } + } + + return null; + } + + /* * There are scenarios when parameterized type is called without type arguments in method signature. * (e.g., public Stack getCurrentSeriesPoints() {}) @@ -1373,6 +1376,7 @@ private static TypeInfo getOriginalInvoker(TypeInfo invokerTypeInfo, } private static Map getInferredFormalTypeParameterMap(TypeInfo invokerTypeInfo, + TypeInfo returnTypeInfo, MethodInfo methodInfo, List argumentTypeInfoList, List typeArgumentTypeInfoList) { @@ -1393,6 +1397,23 @@ private static Map getInferredFormalTypeParameterMap(TypeInfo ); } + if (Objects.nonNull(returnTypeInfo) + && returnTypeInfo.isParameterizedTypeInfo() + && ((ParameterizedTypeInfo) returnTypeInfo).isParameterized() + && returnTypeInfo.getQualifiedClassName().equals(methodInfo.getReturnTypeInfo().getQualifiedClassName())) { + ParameterizedTypeInfo parameterizedTypeInfo = (ParameterizedTypeInfo) returnTypeInfo; + + for (TypeInfo typeArgumentInfo : parameterizedTypeInfo.getTypeArgumentList()) { + if (typeArgumentInfo.isFormalTypeParameterInfo()) { + FormalTypeParameterInfo formalTypeParameterInfo = (FormalTypeParameterInfo) typeArgumentInfo; + + if (!inferredTypeInfoMap.containsKey(formalTypeParameterInfo.getTypeParameter())) { + inferredTypeInfoMap.put(formalTypeParameterInfo.getTypeParameter(), formalTypeParameterInfo.getBaseTypeInfo()); + } + } + } + } + if (!typeArgumentTypeInfoList.isEmpty()) { List formalTypeParameterList = methodInfo.getFormalTypeParameterList(); @@ -1801,12 +1822,17 @@ private static VariableDeclarationDto getVariableDeclarationDto(Set de } } - private static VariableDeclarationDto getClassNameFromVariableMap(String name, - Expression expression, - Map> variableNameMap) { - if (variableNameMap.containsKey(name)) { - int position = expression.getParent().getStartPosition(); - Set variableDeclarationDtoSet = variableNameMap.get(name); + private static VariableDeclarationDto getVariableDeclarationDtoFromVariableMap(String variableName, + Expression expression, + Map> variableNameMap) { + return getVariableDeclarationDtoFromVariableMap(variableName, expression.getParent().getStartPosition(), variableNameMap); + } + + private static VariableDeclarationDto getVariableDeclarationDtoFromVariableMap(String variableName, + int position, + Map> variableNameMap) { + if (variableNameMap.containsKey(variableName)) { + Set variableDeclarationDtoSet = variableNameMap.get(variableName); List selectedVariableDeclarationDto = new ArrayList<>(); for (VariableDeclarationDto vd : variableDeclarationDtoSet) { diff --git a/src/test/java/ca/concordia/jaranalyzer/GuavaV3011TypeInferenceV2APITest.java b/src/test/java/ca/concordia/jaranalyzer/GuavaV3011TypeInferenceV2APITest.java index 58d6542..34ad404 100644 --- a/src/test/java/ca/concordia/jaranalyzer/GuavaV3011TypeInferenceV2APITest.java +++ b/src/test/java/ca/concordia/jaranalyzer/GuavaV3011TypeInferenceV2APITest.java @@ -779,6 +779,26 @@ public boolean visit(MethodInvocation methodInvocation) { }); } + @Test + public void testFormalTypeParameterInferFromVariableDeclarationStatement() { + String filePath = "testProjectDirectory/guava/guava/guava/src/com/google/common/util/concurrent/MoreExecutors.java"; + CompilationUnit compilationUnit = TestUtils.getCompilationUnitFromFile(filePath); + compilationUnit.accept(new ASTVisitor() { + @Override + public boolean visit(MethodInvocation methodInvocation) { + if (methodInvocation.toString().startsWith("TrustedListenableFutureTask.create(command,")) { + MethodInfo methodInfo = TypeInferenceV2API.getMethodInfo(jarInformationSet, javaVersion, methodInvocation); + + assert ("com.google.common.util.concurrent.TrustedListenableFutureTask" + + "::static com.google.common.util.concurrent.TrustedListenableFutureTask create(java.lang.Runnable," + + " java.lang.Void)").equals(methodInfo.toString()); + } + + return true; + } + }); + } + private static void loadTestProjectDirectory(String projectName, String projectUrl, String commitId) { Path projectDirectory = Paths.get("testProjectDirectory").resolve(projectName);