From 3a325ea66bdd07741d1bda43d3c77c2709464ea7 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Mon, 3 Oct 2022 21:12:19 +0200 Subject: [PATCH] #3036 Fix compile errors when intersection types are used in lifecycle methods --- ...eatureCompilationExclusionCliEnhancer.java | 4 + .../test/resources/fullFeatureTest/pom.xml | 1 + .../ap/internal/model/common/Type.java | 29 ++++++ .../internal/model/source/MethodMatcher.java | 3 +- .../LifecycleIntersectionMapper.java | 96 +++++++++++++++++++ .../wildcards/WildCardTest.java | 20 ++++ 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java index e017d1b00..e64b20799 100644 --- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java +++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java @@ -32,6 +32,10 @@ public final class FullFeatureCompilationExclusionCliEnhancer implements Process additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" ); additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" ); + if ( processorType == ProcessorTest.ProcessorType.ECLIPSE_JDT ) { + additionalExcludes.add( + "org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java" ); + } break; case JAVA_9: // TODO find out why this fails: diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml index 8a62f4858..9c17f6c7d 100644 --- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml +++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml @@ -26,6 +26,7 @@ x x x + x diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index fd7f3d690..6d86aad45 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -28,6 +28,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.IntersectionType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -114,6 +115,7 @@ public class Type extends ModelElement implements Comparable { private List alternativeTargetAccessors = null; private Type boundingBase = null; + private List boundTypes = null; private Type boxedEquivalent = null; @@ -354,6 +356,10 @@ public class Type extends ModelElement implements Comparable { return (typeMirror.getKind() == TypeKind.TYPEVAR); } + public boolean isIntersection() { + return typeMirror.getKind() == TypeKind.INTERSECTION; + } + public boolean isJavaLangType() { return packageName != null && packageName.startsWith( "java." ); } @@ -1264,6 +1270,29 @@ public class Type extends ModelElement implements Comparable { return boundingBase; } + public List getTypeBounds() { + if ( this.boundTypes != null ) { + return boundTypes; + } + Type bound = getTypeBound(); + if ( bound == null ) { + this.boundTypes = Collections.emptyList(); + } + else if ( !bound.isIntersection() ) { + this.boundTypes = Collections.singletonList( bound ); + } + else { + List bounds = ( (IntersectionType) bound.typeMirror ).getBounds(); + this.boundTypes = new ArrayList<>( bounds.size() ); + for ( TypeMirror mirror : bounds ) { + boundTypes.add( typeFactory.getType( mirror ) ); + } + } + + return this.boundTypes; + + } + public boolean hasAccessibleConstructor() { if ( hasAccessibleConstructor == null ) { hasAccessibleConstructor = false; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index 42c26f09a..3d8bfea13 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -325,8 +325,7 @@ public class MethodMatcher { */ private boolean candidatesWithinBounds(Map methodParCandidates ) { for ( Map.Entry entry : methodParCandidates.entrySet() ) { - Type bound = entry.getKey().getTypeBound(); - if ( bound != null ) { + for ( Type bound : entry.getKey().getTypeBounds() ) { for ( Type.ResolvedPair pair : entry.getValue().pairs ) { if ( entry.getKey().hasUpperBound() ) { if ( !pair.getMatch().asRawType().isAssignableTo( bound.asRawType() ) ) { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java new file mode 100644 index 000000000..c466ee0d9 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java @@ -0,0 +1,96 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.selection.methodgenerics.wildcards; + +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface LifecycleIntersectionMapper { + + LifecycleIntersectionMapper INSTANCE = Mappers.getMapper( LifecycleIntersectionMapper.class ); + + @Mapping(target = "realm", ignore = true) + RealmTarget mapRealm(String source); + + @Mapping(target = "uniqueRealm", ignore = true) + UniqueRealmTarget mapUniqueRealm(String source); + + @Mapping(target = "realm", ignore = true) + @Mapping(target = "uniqueRealm", ignore = true) + BothRealmsTarget mapBothRealms(String source); + + @AfterMapping + default void afterMapping(String source, @MappingTarget T target) { + target.setRealm( "realm_" + source ); + target.setUniqueRealm( "uniqueRealm_" + source ); + } + + interface RealmObject { + void setRealm(String realm); + } + + interface UniqueRealmObject { + void setUniqueRealm(String realm); + } + + class RealmTarget implements RealmObject { + + protected String realm; + + public String getRealm() { + return realm; + } + + @Override + public void setRealm(String realm) { + this.realm = realm; + } + } + + class UniqueRealmTarget implements UniqueRealmObject { + protected String uniqueRealm; + + @Override + public void setUniqueRealm(String uniqueRealm) { + this.uniqueRealm = uniqueRealm; + } + + public String getUniqueRealm() { + return uniqueRealm; + } + } + + class BothRealmsTarget implements RealmObject, UniqueRealmObject { + + protected String realm; + protected String uniqueRealm; + + public String getRealm() { + return realm; + } + + @Override + public void setRealm(String realm) { + this.realm = realm; + } + + public String getUniqueRealm() { + return uniqueRealm; + } + + @Override + public void setUniqueRealm(String uniqueRealm) { + this.uniqueRealm = uniqueRealm; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java index a228304c7..9e2712963 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.selection.methodgenerics.wildcards; +import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.ProcessorTest; import org.mapstruct.ap.testutil.WithClasses; import org.mapstruct.ap.testutil.runner.Compiler; @@ -54,4 +55,23 @@ public class WildCardTest { assertThat( target ).isNotNull(); assertThat( target.getProp() ).isEqualTo( typeC ); } + + // Eclipse does not handle intersection types correctly (TODO: worthwhile to investigate?) + @ProcessorTest(Compiler.JDK) + @WithClasses(LifecycleIntersectionMapper.class) + @IssueKey("3036") + public void testLifecycleIntersection() { + + LifecycleIntersectionMapper.RealmTarget realmTarget = LifecycleIntersectionMapper.INSTANCE.mapRealm( "test" ); + assertThat( realmTarget.getRealm() ).isNull(); + + LifecycleIntersectionMapper.UniqueRealmTarget uniqueRealmTarget = + LifecycleIntersectionMapper.INSTANCE.mapUniqueRealm( "test" ); + assertThat( uniqueRealmTarget.getUniqueRealm() ).isNull(); + + LifecycleIntersectionMapper.BothRealmsTarget bothRealmsTarget = + LifecycleIntersectionMapper.INSTANCE.mapBothRealms( "test" ); + assertThat( bothRealmsTarget.getRealm() ).isEqualTo( "realm_test" ); + assertThat( bothRealmsTarget.getUniqueRealm() ).isEqualTo( "uniqueRealm_test" ); + } }