diff --git a/build.gradle b/build.gradle index 7169bc2..abe50a4 100644 --- a/build.gradle +++ b/build.gradle @@ -114,6 +114,7 @@ dependencies { testImplementation('org.assertj:assertj-core:3.26.3') testImplementation('org.apache.commons:commons-text:1.12.0') testImplementation( 'junit:junit:4.13.2' ) + testRuntimeOnly('org.immutables:value:2.10.1') } task libs(type: Sync) { @@ -126,6 +127,13 @@ task libs(type: Sync) { rename 'mapstruct-1.5.3.Final.jar', 'mapstruct.jar' } +task testLibs(type: Sync) { + from configurations.testRuntimeClasspath + into "$buildDir/test-libs" + rename 'value-2.10.1.jar', 'immutables.jar' +} + +test.dependsOn( libs, testLibs ) prepareSandbox.dependsOn( libs ) composedJar.dependsOn( libs ) diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java index 330368c..f0ab0a8 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java @@ -34,6 +34,7 @@ abstract class MapstructBaseReference extends BaseReference { private final MapstructBaseReference previous; private final String value; + protected final MapstructUtil mapstructUtil; /** * Create a reference. @@ -47,6 +48,7 @@ abstract class MapstructBaseReference extends BaseReference { super( element, rangeInElement ); this.previous = previous; this.value = value; + this.mapstructUtil = MapstructUtil.getInstance( element.getContainingFile() ); } @Override diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java index c4cc4fd..a46f82d 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java @@ -106,7 +106,7 @@ builderSupportPresent && isBuilderEnabled( getMappingMethod() ) if ( builderSupportPresent ) { for ( PsiMethod method : psiClass.findMethodsByName( value, true ) ) { if ( method.getParameterList().getParametersCount() == 1 && - MapstructUtil.isFluentSetter( method, typeToUse ) ) { + mapstructUtil.isFluentSetter( method, typeToUse ) ) { return method; } } @@ -151,6 +151,7 @@ Object[] getVariantsInternal(@NotNull PsiType psiType) { Map<String, Pair<? extends PsiElement, PsiSubstitutor>> accessors = publicWriteAccessors( psiType, mapStructVersion, + mapstructUtil, mappingMethod ); diff --git a/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java b/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java index 4ada55e..8e19bcc 100644 --- a/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java +++ b/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java @@ -40,16 +40,21 @@ public class FromMapMappingMapTypeInspection extends InspectionBase { @NotNull @Override PsiElementVisitor buildVisitorInternal(@NotNull ProblemsHolder holder, boolean isOnTheFly) { - return new MyJavaElementVisitor( holder, MapstructUtil.resolveMapStructProjectVersion( holder.getFile() ) ); + return new MyJavaElementVisitor( + holder, MapstructUtil.resolveMapStructProjectVersion( holder.getFile() ), + MapstructUtil.getInstance( holder.getFile() ) ); } private static class MyJavaElementVisitor extends JavaElementVisitor { private final ProblemsHolder holder; private final MapStructVersion mapStructVersion; + private final MapstructUtil mapstructUtil; - private MyJavaElementVisitor(ProblemsHolder holder, MapStructVersion mapStructVersion) { - this.holder = holder; - this.mapStructVersion = mapStructVersion; + private MyJavaElementVisitor( + ProblemsHolder holder, MapStructVersion mapStructVersion, MapstructUtil mapstructUtil) { + this.holder = holder; + this.mapStructVersion = mapStructVersion; + this.mapstructUtil = mapstructUtil; } @Override @@ -73,7 +78,8 @@ public void visitMethod(@NotNull PsiMethod method) { if (parameters == null) { return; } - Set<String> allTargetProperties = findAllTargetProperties( targetType, mapStructVersion, method ); + Set<String> allTargetProperties = findAllTargetProperties( + targetType, mapStructVersion, mapstructUtil, method ); if ( allTargetProperties.contains( fromMapMappingParameter.getName() ) ) { return; } diff --git a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java index f01320c..aad5b1c 100644 --- a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java +++ b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java @@ -59,16 +59,21 @@ public class UnmappedTargetPropertiesInspection extends InspectionBase { @NotNull @Override PsiElementVisitor buildVisitorInternal(@NotNull ProblemsHolder holder, boolean isOnTheFly) { - return new MyJavaElementVisitor( holder, MapstructUtil.resolveMapStructProjectVersion( holder.getFile() ) ); + return new MyJavaElementVisitor( + holder, MapstructUtil.resolveMapStructProjectVersion( holder.getFile() ), + MapstructUtil.getInstance( holder.getFile() ) ); } private static class MyJavaElementVisitor extends JavaElementVisitor { private final ProblemsHolder holder; private final MapStructVersion mapStructVersion; + private final MapstructUtil mapstructUtil; - private MyJavaElementVisitor(ProblemsHolder holder, MapStructVersion mapStructVersion) { + private MyJavaElementVisitor( + ProblemsHolder holder, MapStructVersion mapStructVersion, MapstructUtil mapstructUtil) { this.holder = holder; this.mapStructVersion = mapStructVersion; + this.mapstructUtil = mapstructUtil; } @Override @@ -97,7 +102,8 @@ public void visitMethod(PsiMethod method) { return; } - Set<String> allTargetProperties = findAllTargetProperties( targetType, mapStructVersion, method ); + Set<String> allTargetProperties = findAllTargetProperties( + targetType, mapStructVersion, mapstructUtil, method ); // find and remove all defined mapping targets Set<String> definedTargets = findAllDefinedMappingTargets( method, mapStructVersion ) diff --git a/src/main/java/org/mapstruct/intellij/util/DefaultMapstructUtil.java b/src/main/java/org/mapstruct/intellij/util/DefaultMapstructUtil.java new file mode 100644 index 0000000..3618678 --- /dev/null +++ b/src/main/java/org/mapstruct/intellij/util/DefaultMapstructUtil.java @@ -0,0 +1,14 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.intellij.util; + +public class DefaultMapstructUtil extends MapstructUtil { + /** + * Hide constructor. + */ + protected DefaultMapstructUtil() { + } +} diff --git a/src/main/java/org/mapstruct/intellij/util/FreeBuildersMapstructUtil.java b/src/main/java/org/mapstruct/intellij/util/FreeBuildersMapstructUtil.java new file mode 100644 index 0000000..804a0e2 --- /dev/null +++ b/src/main/java/org/mapstruct/intellij/util/FreeBuildersMapstructUtil.java @@ -0,0 +1,40 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.intellij.util; + +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiType; +import org.jetbrains.annotations.NotNull; + +/** + * Mapstruct util for FreeBuilder. + * FreeBuilder adds a lot of other methods that can be considered as fluent setters. Such as: + * <ul> + * <li>{@code from(Target)}</li> + * <li>{@code mapXXX(UnaryOperator)}</li> + * <li>{@code mutateXXX(Consumer)}</li> + * <li>{@code mergeFrom(Target)}</li> + * <li>{@code mergeFrom(Target.Builder)}</li> + * </ul> + * <p> + * When the JavaBean convention is not used with FreeBuilder then the getters are non-standard and MapStruct + * won't recognize them. Therefore, one needs to use the JavaBean convention in which the fluent setters + * start with {@code set}. + */ +public class FreeBuildersMapstructUtil extends MapstructUtil { + /** + * Hide constructor. + */ + protected FreeBuildersMapstructUtil() { + } + + @Override + public boolean isFluentSetter(@NotNull PsiMethod method, PsiType psiType) { + // When using FreeBuilder one needs to use the JavaBean convention, which means that all setters will start + // with set + return false; + } +} diff --git a/src/main/java/org/mapstruct/intellij/util/ImmutablesMapstructUtil.java b/src/main/java/org/mapstruct/intellij/util/ImmutablesMapstructUtil.java new file mode 100644 index 0000000..ec13d6a --- /dev/null +++ b/src/main/java/org/mapstruct/intellij/util/ImmutablesMapstructUtil.java @@ -0,0 +1,28 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.intellij.util; + +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiType; +import org.jetbrains.annotations.NotNull; + +/** + * Mapstruct util for Immutables. + * The generated Immutables also have a from that works as a copy. Our default strategy considers this method + * as a setter with a name {@code from}. Therefore, we are ignoring it. + */ +public class ImmutablesMapstructUtil extends MapstructUtil { + /** + * Hide constructor. + */ + protected ImmutablesMapstructUtil() { + } + + @Override + public boolean isFluentSetter(@NotNull PsiMethod method, PsiType psiType) { + return super.isFluentSetter( method, psiType ) && !method.getName().equals( "from" ); + } +} diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java b/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java index e78be94..8152fb7 100644 --- a/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java +++ b/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java @@ -70,7 +70,7 @@ /** * @author Filip Hrisafov */ -public final class MapstructUtil { +public class MapstructUtil { /** * The FQN of the {@link Mapper} annotation. @@ -101,11 +101,27 @@ public final class MapstructUtil { private static final String CONTEXT_ANNOTATION_FQN = Context.class.getName(); private static final String BUILDER_ANNOTATION_FQN = Builder.class.getName(); private static final String ENUM_MAPPING_ANNOTATION_FQN = EnumMapping.class.getName(); + private static final String IMMUTABLE_FQN = "org.immutables.value.Value.Immutable"; + private static final String FREE_BUILDER_FQN = "org.inferred.freebuilder.FreeBuilder"; /** * Hide constructor. */ - private MapstructUtil() { + MapstructUtil() { + } + + public static MapstructUtil getInstance(@Nullable PsiFile psiFile) { + MapstructUtil mapstructUtil = new DefaultMapstructUtil(); + if (psiFile == null) { + return mapstructUtil; + } + if (MapstructUtil.immutablesOnClassPath( psiFile )) { + mapstructUtil = new ImmutablesMapstructUtil(); + } + else if (MapstructUtil.freeBuilderOnClassPath( psiFile )) { + mapstructUtil = new FreeBuildersMapstructUtil(); + } + return mapstructUtil; } public static LookupElement[] asLookup(Map<String, Pair<? extends PsiElement, PsiSubstitutor>> accessors, @@ -204,7 +220,7 @@ public static boolean isPublicModifiable(@NotNull PsiField field) { !field.hasModifierProperty( PsiModifier.FINAL ); } - public static boolean isFluentSetter(@NotNull PsiMethod method, PsiType psiType) { + public boolean isFluentSetter(@NotNull PsiMethod method, PsiType psiType) { return !psiType.getCanonicalText().startsWith( "java.lang" ) && method.getReturnType() != null && !isAdderWithUpperCase4thCharacter( method ) && @@ -560,6 +576,36 @@ else if ( JavaPsiFacade.getInstance( module.getProject() ) } ); } + public static boolean immutablesOnClassPath(@NotNull PsiFile psiFile) { + Module module = ModuleUtilCore.findModuleForFile( psiFile.getVirtualFile(), psiFile.getProject() ); + if (module == null) { + return false; + } + return CachedValuesManager.getManager( module.getProject() ).getCachedValue( module, () -> { + boolean immutablesOnClassPath = JavaPsiFacade.getInstance( module.getProject() ) + .findClass( IMMUTABLE_FQN, module.getModuleRuntimeScope( false ) ) != null; + return CachedValueProvider.Result.createSingleDependency( + immutablesOnClassPath, + ProjectRootManager.getInstance( module.getProject() ) + ); + } ); + } + + public static boolean freeBuilderOnClassPath(@NotNull PsiFile psiFile) { + Module module = ModuleUtilCore.findModuleForFile( psiFile.getVirtualFile(), psiFile.getProject() ); + if (module == null) { + return false; + } + return CachedValuesManager.getManager( module.getProject() ).getCachedValue( module, () -> { + boolean freeBuilderOnClassPath = JavaPsiFacade.getInstance( module.getProject() ) + .findClass( FREE_BUILDER_FQN, module.getModuleRuntimeScope( false ) ) != null; + return CachedValueProvider.Result.createSingleDependency( + freeBuilderOnClassPath, + ProjectRootManager.getInstance( module.getProject() ) + ); + } ); + } + /** * Checks if MapStruct jdk8 is within the provided module. The MapStruct JDK 8 module is present when the * {@link Mapping} annotation is annotated with {@link java.lang.annotation.Repeatable} diff --git a/src/main/java/org/mapstruct/intellij/util/TargetUtils.java b/src/main/java/org/mapstruct/intellij/util/TargetUtils.java index 5f8c3e5..7ada0b6 100644 --- a/src/main/java/org/mapstruct/intellij/util/TargetUtils.java +++ b/src/main/java/org/mapstruct/intellij/util/TargetUtils.java @@ -46,7 +46,6 @@ import static org.mapstruct.intellij.util.MapstructAnnotationUtils.findMapperConfigReference; import static org.mapstruct.intellij.util.MapstructUtil.MAPPER_ANNOTATION_FQN; import static org.mapstruct.intellij.util.MapstructUtil.canDescendIntoType; -import static org.mapstruct.intellij.util.MapstructUtil.isFluentSetter; import static org.mapstruct.intellij.util.MapstructUtil.isInheritInverseConfiguration; import static org.mapstruct.intellij.util.MapstructUtil.isMapper; import static org.mapstruct.intellij.util.MapstructUtil.isMapperConfig; @@ -100,7 +99,7 @@ public static PsiType getRelevantType(@NotNull PsiMethod mappingMethod) { * @return a stream that holds all public write accessors for the given {@code psiType} */ public static Map<String, Pair<? extends PsiElement, PsiSubstitutor>> publicWriteAccessors(@NotNull PsiType psiType, - MapStructVersion mapStructVersion, PsiMethod mappingMethod) { + MapStructVersion mapStructVersion, MapstructUtil mapstructUtil, PsiMethod mappingMethod) { boolean builderSupportPresent = mapStructVersion.isBuilderSupported(); Pair<PsiClass, TargetType> classAndType = resolveBuilderOrSelfClass( psiType, @@ -116,7 +115,8 @@ builderSupportPresent && isBuilderEnabled( mappingMethod ) TargetType targetType = classAndType.getSecond(); PsiType typeToUse = targetType.type(); - publicWriteAccessors.putAll( publicSetters( psiClass, typeToUse, builderSupportPresent ) ); + publicWriteAccessors.putAll( publicSetters( psiClass, typeToUse, mapstructUtil, + builderSupportPresent && isBuilderEnabled( mappingMethod ) ) ); publicWriteAccessors.putAll( publicFields( psiClass ) ); if ( mapStructVersion.isConstructorSupported() && !targetType.builder() ) { @@ -268,7 +268,7 @@ public static PsiMethod resolveMappingConstructor(@NotNull PsiClass psiClass) { * @return a stream that holds all public setters for the given {@code psiType} */ private static Map<String, Pair<? extends PsiMember, PsiSubstitutor>> publicSetters(@NotNull PsiClass psiClass, - @NotNull PsiType typeToUse, + @NotNull PsiType typeToUse, MapstructUtil mapstructUtil, boolean builderSupportPresent) { Set<PsiMethod> overriddenMethods = new HashSet<>(); Map<String, Pair<? extends PsiMember, PsiSubstitutor>> publicSetters = new LinkedHashMap<>(); @@ -277,7 +277,8 @@ private static Map<String, Pair<? extends PsiMember, PsiSubstitutor>> publicSett if ( method.isConstructor() ) { continue; } - String propertyName = extractPublicSetterPropertyName( method, typeToUse, builderSupportPresent ); + String propertyName = extractPublicSetterPropertyName( + method, typeToUse, mapstructUtil, builderSupportPresent ); if ( propertyName != null && !overriddenMethods.contains( method ) ) { @@ -292,7 +293,7 @@ private static Map<String, Pair<? extends PsiMember, PsiSubstitutor>> publicSett @Nullable private static String extractPublicSetterPropertyName(PsiMethod method, @NotNull PsiType typeToUse, - boolean builderSupportPresent) { + MapstructUtil mapstructUtil, boolean builderSupportPresent) { if (!MapstructUtil.isPublicNonStatic( method )) { // If the method is not public then there is no property return null; @@ -312,7 +313,7 @@ private static String extractPublicSetterPropertyName(PsiMethod method, @NotNull } // This logic is aligned with the DefaultAccessorNamingStrategy - if ( builderSupportPresent && isFluentSetter( method, typeToUse )) { + if ( builderSupportPresent && mapstructUtil.isFluentSetter( method, typeToUse )) { if ( methodName.startsWith( "set" ) && methodName.length() > 3 && Character.isUpperCase( methodName.charAt( 3 ) ) ) { @@ -435,8 +436,8 @@ public static Stream<String> findAllSourcePropertiesForCurrentTarget(@NotNull Ps * @return all target properties for the given {@code targetClass} */ public static Set<String> findAllTargetProperties(@NotNull PsiType targetType, MapStructVersion mapStructVersion, - PsiMethod mappingMethod) { - return publicWriteAccessors( targetType, mapStructVersion, mappingMethod ).keySet(); + MapstructUtil mapstructUtil, PsiMethod mappingMethod) { + return publicWriteAccessors( targetType, mapStructVersion, mapstructUtil, mappingMethod ).keySet(); } /** diff --git a/src/test/java/org/mapstruct/intellij/MapstructBaseCompletionTestCase.java b/src/test/java/org/mapstruct/intellij/MapstructBaseCompletionTestCase.java index 1a48ff9..d805619 100644 --- a/src/test/java/org/mapstruct/intellij/MapstructBaseCompletionTestCase.java +++ b/src/test/java/org/mapstruct/intellij/MapstructBaseCompletionTestCase.java @@ -22,7 +22,8 @@ */ public abstract class MapstructBaseCompletionTestCase extends LightFixtureCompletionTestCase { - private static final String BUILD_LIBS_DIRECTORY = "build/libs"; + private static final String BUILD_LIBS_DIRECTORY = "build/libs"; + protected static final String BUILD_TEST_LIBS_DIRECTORY = "build/test-libs"; @Override protected void setUp() throws Exception { diff --git a/src/test/java/org/mapstruct/intellij/inspection/UnmappedImmutablesFromTargetPropertiesInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/UnmappedImmutablesFromTargetPropertiesInspectionTest.java new file mode 100644 index 0000000..ef4bdac --- /dev/null +++ b/src/test/java/org/mapstruct/intellij/inspection/UnmappedImmutablesFromTargetPropertiesInspectionTest.java @@ -0,0 +1,75 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.intellij.inspection; + +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.testFramework.PsiTestUtil; +import com.intellij.util.PathUtil; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Stephan Leicht Vogt + */ +public class UnmappedImmutablesFromTargetPropertiesInspectionTest extends BaseInspectionTest { + + @NotNull + @Override + protected Class<UnmappedTargetPropertiesInspection> getInspection() { + return UnmappedTargetPropertiesInspection.class; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + final String immutablesLibPath = PathUtil.toSystemIndependentName( new File( BUILD_TEST_LIBS_DIRECTORY ) + .getAbsolutePath() ); + PsiTestUtil.addLibrary( + myFixture.getProjectDisposable(), + myFixture.getModule(), + "Immutables", + immutablesLibPath, + "immutables.jar" + ); + myFixture.copyFileToProject( + "UnmappedImmutablesFromTargetPropertiesData.java", + "org/example/data/UnmappedImmutablesFromTargetPropertiesData.java" + ); + } + + public void testUnmappedImmutablesFromTargetProperties() { + doTest(); + String testName = getTestName( false ); + List<IntentionAction> allQuickFixes = myFixture.getAllQuickFixes(); + + assertThat( allQuickFixes ) + .extracting( IntentionAction::getText ) + .as( "Intent Text" ) + .containsExactly( + "Ignore unmapped target property: 'builderTestName'", + "Add unmapped target property: 'builderTestName'", + + "Ignore unmapped target property: 'targetTestName'", + "Add unmapped target property: 'targetTestName'", + + "Ignore unmapped target property: 'targetTestName'", + "Add unmapped target property: 'targetTestName'", + + "Ignore unmapped target property: 'builderTestName'", + "Add unmapped target property: 'builderTestName'", + + "Ignore unmapped target property: 'targetTestName'", + "Add unmapped target property: 'targetTestName'" + ); + + allQuickFixes.forEach( myFixture::launchAction ); + myFixture.checkResultByFile( testName + "_after.java" ); + } +} diff --git a/testData/inspection/UnmappedImmutablesFromTargetProperties.java b/testData/inspection/UnmappedImmutablesFromTargetProperties.java new file mode 100644 index 0000000..5a236fa --- /dev/null +++ b/testData/inspection/UnmappedImmutablesFromTargetProperties.java @@ -0,0 +1,48 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; +import org.example.data.UnmappedImmutablesFromTargetPropertiesData.Target; + +@Mapper +interface DefaultMapper { + + Target <warning descr="Unmapped target property: builderTestName">map</warning>(String source); +} + +@Mapper(builder = @Builder(disableBuilder = true)) +interface MapperDisabledBuilder { + + Target <warning descr="Unmapped target property: targetTestName">map</warning>(String source); +} + +@Mapper +interface BeanMappingDisabledBuilder { + + @BeanMapping(builder = @Builder(disableBuilder = true)) + Target <warning descr="Unmapped target property: targetTestName">map</warning>(String source); +} + +@Mapper(builder = @Builder(disableBuilder = true)) +interface MapperDisabledBuilderBeanMappingEnabledBuilder { + + @BeanMapping(builder = @Builder(disableBuilder = false)) + Target <warning descr="Unmapped target property: builderTestName">map</warning>(String source); +} + +@MapperConfig(builder = @Builder(disableBuilder = true)) +class DoNotUseBuilderMapperConfig { + +} + +@Mapper(config = DoNotUseBuilderMapperConfig.class) +interface MapperConfigDisabledBuilder { + + Target <warning descr="Unmapped target property: targetTestName">map</warning>(String source); +} diff --git a/testData/inspection/UnmappedImmutablesFromTargetPropertiesData.java b/testData/inspection/UnmappedImmutablesFromTargetPropertiesData.java new file mode 100644 index 0000000..7cddd22 --- /dev/null +++ b/testData/inspection/UnmappedImmutablesFromTargetPropertiesData.java @@ -0,0 +1,43 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +package org.example.data; + +public class UnmappedImmutablesFromTargetPropertiesData { + public static class Target { + + private String targetTestName; + + public String getTargetTestName() { + return targetTestName; + } + + public void setTargetTestName(String targetTestName) { + this.targetTestName = targetTestName; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + public final Builder from(Target instance) { + Objects.requireNonNull(instance, "instance"); + builderTestName(instance.getTargetTestName()); + return this; + } + + public Builder builderTestName(String testName) { + + } + + public Target build() { + return null; + } + } + } + +} diff --git a/testData/inspection/UnmappedImmutablesFromTargetProperties_after.java b/testData/inspection/UnmappedImmutablesFromTargetProperties_after.java new file mode 100644 index 0000000..ec69bf4 --- /dev/null +++ b/testData/inspection/UnmappedImmutablesFromTargetProperties_after.java @@ -0,0 +1,58 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +import org.mapstruct.BeanMapping; +import org.mapstruct.Builder; +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; +import org.example.data.UnmappedImmutablesFromTargetPropertiesData.Target; + +@Mapper +interface DefaultMapper { + + @Mapping(target = "builderTestName", source = "") + @Mapping(target = "builderTestName", ignore = true) + Target map(String source); +} + +@Mapper(builder = @Builder(disableBuilder = true)) +interface MapperDisabledBuilder { + + @Mapping(target = "targetTestName", source = "") + @Mapping(target = "targetTestName", ignore = true) + Target map(String source); +} + +@Mapper +interface BeanMappingDisabledBuilder { + + @Mapping(target = "targetTestName", source = "") + @Mapping(target = "targetTestName", ignore = true) + @BeanMapping(builder = @Builder(disableBuilder = true)) + Target map(String source); +} + +@Mapper(builder = @Builder(disableBuilder = true)) +interface MapperDisabledBuilderBeanMappingEnabledBuilder { + + @Mapping(target = "builderTestName", source = "") + @Mapping(target = "builderTestName", ignore = true) + @BeanMapping(builder = @Builder(disableBuilder = false)) + Target map(String source); +} + +@MapperConfig(builder = @Builder(disableBuilder = true)) +class DoNotUseBuilderMapperConfig { + +} + +@Mapper(config = DoNotUseBuilderMapperConfig.class) +interface MapperConfigDisabledBuilder { + + @Mapping(target = "targetTestName", source = "") + @Mapping(target = "targetTestName", ignore = true) + Target map(String source); +}