diff --git a/src/main/resources/META-INF/jqassistant-rules/java.xml b/src/main/resources/META-INF/jqassistant-rules/java.xml index 243ac93..f04e8db 100644 --- a/src/main/resources/META-INF/jqassistant-rules/java.xml +++ b/src/main/resources/META-INF/jqassistant-rules/java.xml @@ -425,5 +425,25 @@ + + Creates a `DECLARED_BY` relation between a type argument of a parameterized type to the according type parameter of the declaring type. + + (rawType:Type), + (parameterizedType)-[hasActualTypeArgument:HAS_ACTUAL_TYPE_ARGUMENT]->(typeArgument), + (rawType)-[declaresTypeParameter:DECLARES_TYPE_PARAMETER]->(typeParameter) + WHERE + hasActualTypeArgument.index = declaresTypeParameter.index + MERGE + (typeArgument)-[:DECLARED_BY]->(typeParameter) + RETURN + count(*) as TypeParameterDeclarations + ]]> + + + + + diff --git a/src/test/java/com/buschmais/jqassistant/plugin/java/test/rules/TypeParameterIT.java b/src/test/java/com/buschmais/jqassistant/plugin/java/test/rules/TypeParameterIT.java index 1b0be67..173f812 100644 --- a/src/test/java/com/buschmais/jqassistant/plugin/java/test/rules/TypeParameterIT.java +++ b/src/test/java/com/buschmais/jqassistant/plugin/java/test/rules/TypeParameterIT.java @@ -1,7 +1,10 @@ package com.buschmais.jqassistant.plugin.java.test.rules; import java.util.List; +import java.util.Map; +import com.buschmais.jqassistant.core.report.api.model.Result; +import com.buschmais.jqassistant.core.rule.api.model.Concept; import com.buschmais.jqassistant.core.rule.api.model.RuleException; import com.buschmais.jqassistant.plugin.java.api.model.TypeDescriptor; import com.buschmais.jqassistant.plugin.java.test.AbstractJavaPluginIT; @@ -10,15 +13,20 @@ import org.junit.jupiter.api.Test; +import static com.buschmais.jqassistant.core.report.api.model.Result.Status.SUCCESS; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.HamcrestCondition.matching; public class TypeParameterIT extends AbstractJavaPluginIT { @Test - void outerClassTypeParameters() throws RuleException { + void innerTypeParameterDeclaredByOuterType() throws RuleException { scanClasses(GenericTypeDeclarations.class, GenericTypeDeclarations.Inner.class); - applyConcept("java:InnerTypeParameterDeclaredByOuterType"); + Result conceptResult = applyConcept("java:InnerTypeParameterDeclaredByOuterType"); + assertThat(conceptResult.getStatus()).isEqualTo(SUCCESS); + List> rows = conceptResult.getRows(); + assertThat(rows).hasSize(1); + assertThat(rows.get(0).get("OuterTypeDeclarations")).isEqualTo(1l); store.beginTransaction(); List outerTypes = query( "MATCH (:Type{name:'GenericTypeDeclarations$Inner'})-[:REQUIRES_TYPE_PARAMETER]->(:TypeVariable)-[:DECLARED_BY]->(:TypeVariable)<-[:DECLARES_TYPE_PARAMETER]-(outer:Type) RETURN outer") @@ -27,4 +35,23 @@ void outerClassTypeParameters() throws RuleException { assertThat(outerTypes.get(0)).is(matching(TypeDescriptorMatcher.typeDescriptor(GenericTypeDeclarations.class))); store.commitTransaction(); } + + @Test + void typeArgumentDeclaredByTypeParameter() throws RuleException { + scanClasses(GenericTypeDeclarations.class, GenericTypeDeclarations.Inner.class); + Result conceptResult = applyConcept("java:TypeArgumentDeclaredByTypeParameter"); + assertThat(conceptResult.getStatus()).isEqualTo(SUCCESS); + List> rows = conceptResult.getRows(); + assertThat(rows).hasSize(1); + assertThat(rows.get(0).get("TypeParameterDeclarations")).isEqualTo(2l); + store.beginTransaction(); + List typeVariables = query( // + "MATCH (:Type{name:'GenericTypeDeclarations$Inner'})-[:EXTENDS_GENERIC]->(parameterizedType:ParameterizedType)," + // + "(:Type{name:'GenericTypeDeclarations'})-[dtp:DECLARES_TYPE_PARAMETER]->(typeVariable:TypeVariable)," + // + "(parameterizedType)-[:HAS_ACTUAL_TYPE_ARGUMENT]->(:TypeVariable)-[:DECLARED_BY]->(typeVariable:TypeVariable) " + // + "RETURN typeVariable.name as typeVariableName ORDER BY dtp.index").getColumn("typeVariableName"); + assertThat(typeVariables).hasSize(2); + assertThat(typeVariables).containsExactly("X", "Y"); + store.commitTransaction(); + } }