diff --git a/README.md b/README.md
index 000ea87..bd55e48 100644
--- a/README.md
+++ b/README.md
@@ -24,11 +24,13 @@ To learn more about MapStruct have a look at the [mapstruct](https://github.com/
* Code completions
* Completion of `target` and `source` properties in `@Mapping` annotation (nested properties also work)
* Completion of `target` and `source` properties in `@ValueMapping` annotation
+ * Completion of `targets` in `@Ignored` annotation with `prefix` support
* Completion of `componentModel` in `@Mapper` and `@MapperConfig` annotations
* Completion of `qualifiedByName` in `@Mapping` annotation
* Go To Declaration for properties in `target` and `source` to setters / getters
+* Go To Declaration for `targets` in `@Ignored` annotation
* Go To Declaration for `Mapping#qualifiedByName`
-* Find usages of properties in `target` and `source` and find usages of setters / getters in `@Mapping` annotations
+* Find usages of properties in `target` and `source` and find usages of setters / getters in `@Mapping` and `@Ignored` annotations
* Highlighting properties in `target` and `source`
* Errors and Quick fixes:
* `@Mapper` or `@MapperConfig` annotation missing
@@ -37,9 +39,10 @@ To learn more about MapStruct have a look at the [mapstruct](https://github.com/
* No `source` defined in `@Mapping` annotation
* More than one `source` in `@Mapping` annotation defined with quick fixes: Remove `source`. Remove `constant`. Remove `expression`. Use `constant` as `defaultValue`. Use `expression` as `defaultExpression`.
* More than one default source in `@Mapping` annotation defined with quick fixes: Remove `defaultValue`. Remove `defaultExpression`.
- * `target` mapped more than once by `@Mapping` annotations with quick fixes: Remove annotation and change target property.
+ * `target` mapped more than once by `@Mapping` and/or `@Ignored` annotations with quick fixes: Remove annotation and change target property.
* `*` used as a source in `@Mapping` annotation with quick fixes: Replace `*` with method parameter name.
- * Unknown reference inspection for `source` and `target` in `@Mapping` and `@ValueMapping` annotation.
+ * Unknown reference inspection for `source` and `target` in `@Mapping` and `@ValueMapping` annotation.
+ * Unknown reference inspection for `targets` and `prefix` in `@Ignored` annotation.
* Unknown reference inspection for `qualifiedByName` in `@Mapping` annotation
## Requirements
diff --git a/build.gradle b/build.gradle
index d21b9d8..9931651 100644
--- a/build.gradle
+++ b/build.gradle
@@ -111,13 +111,13 @@ dependencies {
testFramework( TestFrameworkType.Platform.INSTANCE )
testFramework( TestFrameworkType.Bundled.INSTANCE )
}
- implementation('org.mapstruct:mapstruct:1.5.3.Final')
+ implementation('org.mapstruct:mapstruct:1.7.0.Beta1')
testImplementation(platform('org.junit:junit-bom:5.11.0'))
testImplementation('org.junit.platform:junit-platform-launcher')
testImplementation('org.junit.jupiter:junit-jupiter-api')
testImplementation('org.junit.jupiter:junit-jupiter-engine')
testRuntimeOnly('org.junit.vintage:junit-vintage-engine')
- testImplementation('org.assertj:assertj-core:3.26.3')
+ testImplementation('org.assertj:assertj-core:3.27.7')
testImplementation('org.apache.commons:commons-text:1.15.0')
testImplementation( 'junit:junit:4.13.2' )
testRuntimeOnly('org.immutables:value:2.10.1')
@@ -130,7 +130,9 @@ tasks.register('libs', Sync) {
include('mapstruct-intellij-*.jar')
include('MapStruct-Intellij-*.jar')
}
- rename('mapstruct-1.5.3.Final.jar', 'mapstruct.jar')
+ rename { String fileName ->
+ return fileName.startsWith('mapstruct-') ? 'mapstruct.jar' : fileName
+ }
}
tasks.register('testLibs', Sync) {
diff --git a/change-notes.html b/change-notes.html
index 722f1a3..9956473 100644
--- a/change-notes.html
+++ b/change-notes.html
@@ -1,4 +1,16 @@
+
1.10.0
+
+ - Support for
@Ignored annotation (MapStruct 1.7):
+
+ - Code completion for
targets in @Ignored annotation with prefix support
+ - Go to declaration and find usages for
@Ignored target properties
+ - Unknown reference inspection for
targets and prefix in @Ignored annotation
+ - Unmapped target properties inspection considers
@Ignored targets
+ - Target mapped more than once inspection detects conflicts between
@Mapping and @Ignored
+
+
+
1.9.1
- Improve error messages for reference inspection
diff --git a/description.html b/description.html
index 7c30808..7ddf23b 100644
--- a/description.html
+++ b/description.html
@@ -23,12 +23,14 @@
- Completion of
target and source properties in @Mapping annotation (nested properties also work)
- Completion of
target and source properties in @ValueMapping annotation
+ - Completion of
targets in @Ignored annotation with prefix support
- Completion of
componentModel in @Mapper and @MapperConfig annotations
- Completion of
qualifiedByName in @Mapping annotation
- Go To Declaration for properties in
target and source to setters / getters
- - Find usages of properties in
target and source and find usages of setters / getters in @Mapping annotations
+ - Go To Declaration for
targets in @Ignored annotation
+ - Find usages of properties in
target and source and find usages of setters / getters in @Mapping and @Ignored annotations
- Go To Declaration for
Mapping#qualifiedByName
- Highlighting properties in
target and source
- Refactoring support for properties and methods renaming
@@ -41,9 +43,10 @@
- No
source defined in @Mapping annotation
- More than one
source in @Mapping annotation defined with quick fixes: Remove source. Remove constant. Remove expression. Use constant as defaultValue. Use expression as defaultExpression.
- More than one default source in
@Mapping annotation defined with quick fixes: Remove defaultValue. Remove defaultExpression.
- target mapped more than once by @Mapping annotations with quick fixes: Remove annotation and change target property.
+ target mapped more than once by @Mapping and/or @Ignored annotations with quick fixes: Remove annotation and change target property.
* used as a source in @Mapping annotations with quick fixes: Replace * with method parameter name.
- - Unknown reference inspection for
source and target in @Mapping and @ValueMapping annotation.
+ - Unknown reference inspection for
source and target in @Mapping and @ValueMapping annotation.
+ - Unknown reference inspection for
targets and prefix in @Ignored annotation.
- Unknown reference inspection for
qualifiedByName in @Mapping annotation.
diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseTargetReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseTargetReference.java
new file mode 100644
index 0000000..efa2974
--- /dev/null
+++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseTargetReference.java
@@ -0,0 +1,209 @@
+/*
+ * 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.codeinsight.references;
+
+import java.util.Map;
+import java.util.stream.Stream;
+
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParameterList;
+import com.intellij.psi.PsiRecordComponent;
+import com.intellij.psi.PsiSubstitutor;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.PsiVariable;
+import com.intellij.psi.util.PsiUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.mapstruct.intellij.util.MapStructVersion;
+import org.mapstruct.intellij.util.MapstructUtil;
+import org.mapstruct.intellij.util.TargetType;
+import org.mapstruct.intellij.util.TargetUtils;
+
+import static org.mapstruct.intellij.util.MapstructUtil.asLookup;
+import static org.mapstruct.intellij.util.MapstructUtil.findRecordComponent;
+import static org.mapstruct.intellij.util.MapstructUtil.isPublicModifiable;
+import static org.mapstruct.intellij.util.MapstructUtil.isPublicNonStatic;
+import static org.mapstruct.intellij.util.TargetUtils.findAllDefinedMappingTargets;
+import static org.mapstruct.intellij.util.TargetUtils.findAllIgnoredTargets;
+import static org.mapstruct.intellij.util.TargetUtils.isBuilderEnabled;
+import static org.mapstruct.intellij.util.TargetUtils.publicWriteAccessors;
+import static org.mapstruct.intellij.util.TargetUtils.resolveBuilderOrSelfClass;
+import static org.mapstruct.intellij.util.TypeUtils.firstParameterPsiType;
+
+/**
+ * Base class for target references ({@link MapstructTargetReference} and {@link MapstructIgnoredTargetReference}).
+ * Provides shared implementations for resolving the type of target elements.
+ *
+ * @author Filip Hrisafov
+ */
+abstract class BaseTargetReference extends BaseMappingReference {
+
+ protected final MapStructVersion mapStructVersion;
+
+ BaseTargetReference(@NotNull PsiElement element, @Nullable MapstructBaseReference previousReference,
+ TextRange rangeInElement, String value) {
+ super( element, previousReference, rangeInElement, value );
+ mapStructVersion = MapstructUtil.resolveMapStructProjectVersion( element.getContainingFile()
+ .getOriginalFile() );
+ }
+
+ @Override
+ PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) {
+ return resolveTargetElement( value, psiType, getMappingMethod() );
+ }
+
+ PsiElement resolveTargetElement(@NotNull String value, @NotNull PsiType psiType,
+ @Nullable PsiMethod mappingMethod) {
+ boolean builderSupportPresent = mapStructVersion.isBuilderSupported();
+ Pair pair = resolveBuilderOrSelfClass(
+ psiType,
+ builderSupportPresent && isBuilderEnabled( mappingMethod )
+ );
+ if ( pair == null ) {
+ return null;
+ }
+
+ PsiClass psiClass = pair.getFirst();
+ TargetType targetType = pair.getSecond();
+ PsiType typeToUse = targetType.type();
+
+ PsiRecordComponent recordComponent = findRecordComponent( value, psiClass );
+ if ( recordComponent != null ) {
+ return recordComponent;
+ }
+
+ if ( mapStructVersion.isConstructorSupported() && !targetType.builder() ) {
+ PsiMethod constructor = TargetUtils.resolveMappingConstructor( psiClass );
+ if ( constructor != null && constructor.hasParameters() ) {
+ for ( PsiParameter parameter : constructor.getParameterList().getParameters() ) {
+ if ( value.equals( parameter.getName() ) ) {
+ return parameter;
+ }
+ }
+ }
+ }
+
+ String capitalizedName = MapstructUtil.capitalize( value );
+ PsiMethod[] methods = psiClass.findMethodsByName( "set" + capitalizedName, true );
+ if ( methods.length != 0 && isPublicNonStatic( methods[0] ) ) {
+ return methods[0];
+ }
+
+ // If there is no such setter we need to check if there is a collection getter
+ methods = psiClass.findMethodsByName( "get" + capitalizedName, true );
+ if ( methods.length != 0 && isCollectionGetterWriteAccessor( methods[0] ) ) {
+ return methods[0];
+ }
+
+ if ( builderSupportPresent ) {
+ for ( Pair builderPair : psiClass.findMethodsAndTheirSubstitutorsByName(
+ value,
+ true
+ ) ) {
+ PsiMethod method = builderPair.getFirst();
+ if ( method.getParameterList().getParametersCount() == 1 &&
+ mapstructUtil.isFluentSetter( method, typeToUse, builderPair.getSecond() ) ) {
+ return method;
+ }
+ }
+ }
+
+ PsiClass selfClass = PsiUtil.resolveClassInType( psiType );
+ if ( selfClass != null ) {
+ PsiField field = selfClass.findFieldByName( value, true );
+ if ( field != null && isPublicModifiable( field ) ) {
+ return field;
+ }
+ }
+
+ return null;
+ }
+
+ @NotNull
+ @Override
+ Object[] getVariantsInternal(@NotNull PsiType psiType) {
+
+ PsiMethod mappingMethod = getMappingMethod();
+
+ Map> accessors = publicWriteAccessors(
+ psiType,
+ mapStructVersion,
+ mapstructUtil,
+ mappingMethod
+ );
+
+ if ( mappingMethod != null ) {
+ findAllDefinedTargets( mappingMethod ).forEach( accessors::remove );
+ }
+
+ return asLookup(
+ accessors,
+ BaseTargetReference::memberPsiType
+ );
+ }
+
+ protected Stream findAllDefinedTargets(PsiMethod mappingMethod) {
+ return Stream.concat(
+ findAllDefinedMappingTargets( mappingMethod, mapStructVersion ),
+ findAllIgnoredTargets( mappingMethod )
+ );
+ }
+
+ @Nullable
+ @Override
+ PsiType resolvedType() {
+ PsiElement element = resolve();
+ if ( element instanceof PsiMethod psiMethod ) {
+ return firstParameterPsiType( psiMethod );
+ }
+ else if ( element instanceof PsiParameter psiParameter ) {
+ return psiParameter.getType();
+ }
+ else if ( element instanceof PsiRecordComponent psiRecordComponent ) {
+ return psiRecordComponent.getType();
+ }
+ else if ( element instanceof PsiField psiField ) {
+ return psiField.getType();
+ }
+
+ return null;
+ }
+
+ static PsiType memberPsiType(PsiElement psiMember) {
+ if ( psiMember instanceof PsiMethod psiMemberMethod ) {
+ return resolveMethodType( psiMemberMethod );
+ }
+ else if ( psiMember instanceof PsiVariable psiMemberVariable ) {
+ return psiMemberVariable.getType();
+ }
+ return null;
+ }
+
+ static PsiType resolveMethodType(PsiMethod psiMethod) {
+ PsiParameter[] psiParameters = psiMethod.getParameterList().getParameters();
+ if ( psiParameters.length == 0 ) {
+ return psiMethod.getReturnType();
+ }
+ return psiParameters[0].getType();
+ }
+
+ private static boolean isCollectionGetterWriteAccessor(@NotNull PsiMethod method) {
+ if ( !isPublicNonStatic( method ) ) {
+ return false;
+ }
+ PsiParameterList parameterList = method.getParameterList();
+ if ( parameterList.getParametersCount() > 0 ) {
+ return false;
+ }
+ return TargetUtils.isMethodReturnTypeAssignableToCollectionOrMap( method );
+ }
+}
diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructIgnoredTargetReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructIgnoredTargetReference.java
new file mode 100644
index 0000000..18fdc00
--- /dev/null
+++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructIgnoredTargetReference.java
@@ -0,0 +1,108 @@
+/*
+ * 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.codeinsight.references;
+
+import java.util.stream.Stream;
+
+import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.util.PsiTreeUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.mapstruct.intellij.util.TargetUtils;
+
+import static org.mapstruct.intellij.util.MapstructUtil.canDescendIntoType;
+import static org.mapstruct.intellij.util.TargetUtils.getRelevantType;
+
+/**
+ * Reference for {@link org.mapstruct.Ignored#targets()}.
+ *
+ * @author Filip Hrisafov
+ */
+class MapstructIgnoredTargetReference extends BaseTargetReference {
+
+ private MapstructIgnoredTargetReference(PsiElement element, MapstructIgnoredTargetReference previousReference,
+ TextRange rangeInElement, String value) {
+ super( element, previousReference, rangeInElement, value );
+ }
+
+ @Nullable
+ @Override
+ PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMethod) {
+ PsiType targetType = resolveIgnoredTargetsBaseType( mappingMethod );
+ return targetType == null ? null : resolveInternal( value, targetType );
+ }
+
+ @Override
+ protected Stream findAllDefinedTargets(PsiMethod mappingMethod) {
+ PsiAnnotation annotation = PsiTreeUtil.getParentOfType( getElement(), PsiAnnotation.class );
+
+ Stream allTargets = super.findAllDefinedTargets( mappingMethod );
+
+ String prefixDot = TargetUtils.getIgnoredPrefix( annotation );
+
+ if ( !prefixDot.isEmpty() ) {
+ allTargets = allTargets
+ .filter( target -> target.startsWith( prefixDot ) )
+ .map( target -> target.substring( prefixDot.length() ) );
+ }
+ return allTargets;
+ }
+
+ @NotNull
+ @Override
+ Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) {
+ PsiType targetType = resolveIgnoredTargetsBaseType( mappingMethod );
+ return targetType == null ? LookupElement.EMPTY_ARRAY : getVariantsInternal( targetType );
+ }
+
+ static PsiReference[] create(PsiElement psiElement) {
+ return MapstructBaseReference.create( psiElement, MapstructIgnoredTargetReference::new, true );
+ }
+
+ @Nullable
+ private PsiType resolveIgnoredTargetsBaseType(@NotNull PsiMethod mappingMethod) {
+ PsiType targetType = getRelevantType( mappingMethod );
+ if ( targetType == null ) {
+ return null;
+ }
+
+ PsiAnnotation annotation = PsiTreeUtil.getParentOfType( getElement(), PsiAnnotation.class );
+ if ( annotation == null ) {
+ return targetType;
+ }
+
+ String prefix = AnnotationUtil.getDeclaredStringAttributeValue( annotation, "prefix" );
+ if ( prefix == null || prefix.isEmpty() ) {
+ return targetType;
+ }
+
+ PsiElement prefixElement = annotation.findDeclaredAttributeValue( "prefix" );
+ if ( prefixElement == null ) {
+ return targetType;
+ }
+
+ PsiReference[] prefixReferences = MapstructTargetReference.create( prefixElement );
+ if ( prefixReferences.length == 0 ) {
+ return null;
+ }
+
+ PsiReference lastReference = prefixReferences[prefixReferences.length - 1];
+ if ( lastReference instanceof MapstructBaseReference baseReference ) {
+ PsiType resolvedType = baseReference.resolvedType();
+ return canDescendIntoType( resolvedType ) ? resolvedType : null;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructKotlinReferenceContributor.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructKotlinReferenceContributor.java
index 68155d5..4dc15d4 100644
--- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructKotlinReferenceContributor.java
+++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructKotlinReferenceContributor.java
@@ -10,6 +10,7 @@
import org.jetbrains.annotations.NotNull;
import static org.mapstruct.intellij.util.MapstructKotlinElementUtils.beanMappingElementPattern;
+import static org.mapstruct.intellij.util.MapstructKotlinElementUtils.ignoredElementPattern;
import static org.mapstruct.intellij.util.MapstructKotlinElementUtils.mappingElementPattern;
import static org.mapstruct.intellij.util.MapstructKotlinElementUtils.valueMappingElementPattern;
@@ -31,6 +32,14 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar)
mappingElementPattern( "source" ),
new MappingTargetReferenceProvider( MapstructSourceReference::create )
);
+ registrar.registerReferenceProvider(
+ ignoredElementPattern( "prefix" ),
+ new MappingTargetReferenceProvider( MapstructTargetReference::create )
+ );
+ registrar.registerReferenceProvider(
+ ignoredElementPattern( "targets" ),
+ new MappingTargetReferenceProvider( MapstructIgnoredTargetReference::create )
+ );
registrar.registerReferenceProvider(
beanMappingElementPattern( "ignoreUnmappedSourceProperties" ),
new MappingTargetReferenceProvider( MapstructSourceReference::createNonNested )
diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructReferenceContributor.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructReferenceContributor.java
index f2edb1f..d097b94 100644
--- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructReferenceContributor.java
+++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructReferenceContributor.java
@@ -10,6 +10,7 @@
import org.jetbrains.annotations.NotNull;
import static org.mapstruct.intellij.util.MapstructElementUtils.beanMappingElementPattern;
+import static org.mapstruct.intellij.util.MapstructElementUtils.ignoredElementPattern;
import static org.mapstruct.intellij.util.MapstructElementUtils.mappingElementPattern;
import static org.mapstruct.intellij.util.MapstructElementUtils.valueMappingElementPattern;
@@ -34,6 +35,14 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar)
mappingElementPattern( "source" ),
new MappingTargetReferenceProvider( MapstructSourceReference::create )
);
+ registrar.registerReferenceProvider(
+ ignoredElementPattern( "prefix" ),
+ new MappingTargetReferenceProvider( MapstructTargetReference::create )
+ );
+ registrar.registerReferenceProvider(
+ ignoredElementPattern( "targets" ),
+ new MappingTargetReferenceProvider( MapstructIgnoredTargetReference::create )
+ );
registrar.registerReferenceProvider(
beanMappingElementPattern( "ignoreUnmappedSourceProperties" ),
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 ca54a8a..3bb57bb 100644
--- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java
+++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java
@@ -5,53 +5,26 @@
*/
package org.mapstruct.intellij.codeinsight.references;
-import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
-import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.lookup.LookupElement;
-import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
-import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
-import com.intellij.psi.PsiParameter;
-import com.intellij.psi.PsiParameterList;
-import com.intellij.psi.PsiRecordComponent;
import com.intellij.psi.PsiReference;
-import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiType;
-import com.intellij.psi.PsiVariable;
-import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.mapstruct.Mapping;
-import org.mapstruct.intellij.util.MapStructVersion;
import org.mapstruct.intellij.util.MapstructUtil;
-import org.mapstruct.intellij.util.TargetType;
-import org.mapstruct.intellij.util.TargetUtils;
-import static org.mapstruct.intellij.util.MapstructAnnotationUtils.findAllDefinedMappingAnnotations;
-import static org.mapstruct.intellij.util.MapstructUtil.asLookup;
-import static org.mapstruct.intellij.util.MapstructUtil.findRecordComponent;
-import static org.mapstruct.intellij.util.MapstructUtil.isPublicModifiable;
-import static org.mapstruct.intellij.util.MapstructUtil.isPublicNonStatic;
import static org.mapstruct.intellij.util.TargetUtils.getRelevantType;
-import static org.mapstruct.intellij.util.TargetUtils.isBuilderEnabled;
-import static org.mapstruct.intellij.util.TargetUtils.publicWriteAccessors;
-import static org.mapstruct.intellij.util.TargetUtils.resolveBuilderOrSelfClass;
-import static org.mapstruct.intellij.util.TypeUtils.firstParameterPsiType;
/**
* Reference for {@link org.mapstruct.Mapping#target()}.
*
* @author Filip Hrisafov
*/
-class MapstructTargetReference extends BaseMappingReference {
-
- private final MapStructVersion mapStructVersion;
+class MapstructTargetReference extends BaseTargetReference {
/**
* Create a new {@link MapstructTargetReference} with the provided parameters
@@ -64,75 +37,6 @@ class MapstructTargetReference extends BaseMappingReference {
private MapstructTargetReference(PsiElement element, MapstructTargetReference previousReference,
TextRange rangeInElement, String value) {
super( element, previousReference, rangeInElement, value );
- mapStructVersion = MapstructUtil.resolveMapStructProjectVersion( element.getContainingFile()
- .getOriginalFile() );
- }
-
- @Override
- PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) {
- boolean builderSupportPresent = mapStructVersion.isBuilderSupported();
- Pair pair = resolveBuilderOrSelfClass(
- psiType,
- builderSupportPresent && isBuilderEnabled( getMappingMethod() )
- );
- if ( pair == null ) {
- return null;
- }
-
- PsiClass psiClass = pair.getFirst();
- TargetType targetType = pair.getSecond();
- PsiType typeToUse = targetType.type();
-
- PsiRecordComponent recordComponent = findRecordComponent( value, psiClass );
- if ( recordComponent != null ) {
- return recordComponent;
- }
-
- if ( mapStructVersion.isConstructorSupported() && !targetType.builder() ) {
- PsiMethod constructor = TargetUtils.resolveMappingConstructor( psiClass );
- if ( constructor != null && constructor.hasParameters() ) {
- for ( PsiParameter parameter : constructor.getParameterList().getParameters() ) {
- if ( value.equals( parameter.getName() ) ) {
- return parameter;
- }
- }
- }
- }
-
- String capitalizedName = MapstructUtil.capitalize( value );
- PsiMethod[] methods = psiClass.findMethodsByName( "set" + capitalizedName, true );
- if ( methods.length != 0 && isPublicNonStatic( methods[0] ) ) {
- return methods[0];
- }
-
- // If there is no such setter we need to check if there is a collection getter
- methods = psiClass.findMethodsByName( "get" + capitalizedName, true );
- if ( methods.length != 0 && isCollectionGetterWriteAccessor( methods[0] ) ) {
- return methods[0];
- }
-
- if ( builderSupportPresent ) {
- for ( Pair builderPair : psiClass.findMethodsAndTheirSubstitutorsByName(
- value,
- true
- ) ) {
- PsiMethod method = builderPair.getFirst();
- if ( method.getParameterList().getParametersCount() == 1 &&
- mapstructUtil.isFluentSetter( method, typeToUse, builderPair.getSecond() ) ) {
- return method;
- }
- }
- }
-
- PsiClass selfClass = PsiUtil.resolveClassInType( psiType );
- if ( selfClass != null ) {
- PsiField field = selfClass.findFieldByName( value, true );
- if ( field != null && isPublicModifiable( field ) ) {
- return field;
- }
- }
-
- return null;
}
@Override
@@ -142,7 +46,11 @@ PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMeth
return null;
}
- PsiElement psiElement = resolveInternal( value, relevantType );
+ PsiElement psiElement = resolveTargetElement(
+ value,
+ relevantType,
+ mappingMethod
+ );
if ( psiElement != null ) {
return psiElement;
}
@@ -154,44 +62,6 @@ PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMeth
.orElse( null );
}
- @NotNull
- @Override
- Object[] getVariantsInternal(@NotNull PsiType psiType) {
-
- PsiMethod mappingMethod = getMappingMethod();
-
- Map> accessors = publicWriteAccessors(
- psiType,
- mapStructVersion,
- mapstructUtil,
- mappingMethod
- );
-
- if (mappingMethod != null) {
- Stream allDefinedMappingTargets = findAllDefinedMappingTargets( mappingMethod );
- allDefinedMappingTargets.forEach( accessors::remove );
- }
-
- return asLookup(
- accessors,
- MapstructTargetReference::memberPsiType
- );
- }
-
- /**
- * Find all defined {@link Mapping#target()} for the given method
- *
- * @param method that needs to be checked
- *
- * @return see description
- */
- private Stream findAllDefinedMappingTargets(@NotNull PsiMethod method) {
- return findAllDefinedMappingAnnotations( method, mapStructVersion )
- .map( psiAnnotation -> AnnotationUtil.getDeclaredStringAttributeValue( psiAnnotation, "target" ) )
- .filter( Objects::nonNull )
- .filter( s -> !s.isEmpty() );
- }
-
@NotNull
@Override
Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) {
@@ -199,27 +69,6 @@ Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) {
return targetType == null ? LookupElement.EMPTY_ARRAY : getVariantsInternal( targetType );
}
- @Nullable
- @Override
- PsiType resolvedType() {
- PsiElement element = resolve();
-
- if ( element instanceof PsiMethod psiMethod ) {
- return firstParameterPsiType( psiMethod );
- }
- else if ( element instanceof PsiParameter psiParameter ) {
- return psiParameter.getType();
- }
- else if ( element instanceof PsiRecordComponent psiRecordComponent ) {
- return psiRecordComponent.getType();
- }
- else if ( element instanceof PsiField psiField ) {
- return psiField.getType();
- }
-
- return null;
- }
-
/**
* @param psiElement the literal for which references need to be created
*
@@ -229,35 +78,4 @@ static PsiReference[] create(PsiElement psiElement) {
return MapstructBaseReference.create( psiElement, MapstructTargetReference::new, true );
}
- private static PsiType memberPsiType(PsiElement psiMember) {
- if ( psiMember instanceof PsiMethod psiMemberMethod ) {
- return resolveMethodType( psiMemberMethod );
- }
- else if ( psiMember instanceof PsiVariable psiMemberVariable ) {
- return psiMemberVariable.getType();
- }
- else {
- return null;
- }
-
- }
-
- private static boolean isCollectionGetterWriteAccessor(@NotNull PsiMethod method) {
- if ( !isPublicNonStatic( method ) ) {
- return false;
- }
- PsiParameterList parameterList = method.getParameterList();
- if ( parameterList.getParametersCount() > 0 ) {
- return false;
- }
- return TargetUtils.isMethodReturnTypeAssignableToCollectionOrMap( method );
- }
-
- private static PsiType resolveMethodType(PsiMethod psiMethod) {
- PsiParameter[] psiParameters = psiMethod.getParameterList().getParameters();
- if ( psiParameters.length == 0 ) {
- return psiMethod.getReturnType();
- }
- return psiParameters[0].getType();
- }
}
diff --git a/src/main/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspection.java b/src/main/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspection.java
index 2c5aade..9fa63bd 100644
--- a/src/main/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspection.java
+++ b/src/main/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspection.java
@@ -5,6 +5,14 @@
*/
package org.mapstruct.intellij.inspection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
@@ -38,15 +46,10 @@
import org.mapstruct.intellij.util.MapstructUtil;
import org.mapstruct.intellij.util.TargetUtils;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
import static com.intellij.codeInsight.AnnotationUtil.getStringAttributeValue;
-import static org.mapstruct.intellij.util.MapstructAnnotationUtils.extractMappingAnnotationsFromMappings;
+import static org.mapstruct.intellij.util.MapstructAnnotationUtils.extractRepeatableAnnotations;
+import static org.mapstruct.intellij.util.MapstructUtil.IGNORED_ANNOTATION_FQN;
+import static org.mapstruct.intellij.util.MapstructUtil.IGNORED_LIST_ANNOTATION_FQN;
import static org.mapstruct.intellij.util.MapstructUtil.MAPPINGS_ANNOTATION_FQN;
import static org.mapstruct.intellij.util.MapstructUtil.MAPPING_ANNOTATION_FQN;
import static org.mapstruct.intellij.util.TargetUtils.getTargetType;
@@ -87,9 +90,16 @@ public void visitMethod(PsiMethod method) {
handleMappingAnnotation( psiAnnotation, problemMap );
}
else if (MAPPINGS_ANNOTATION_FQN.equals( qualifiedName )) {
- extractMappingAnnotationsFromMappings( psiAnnotation )
+ extractRepeatableAnnotations( psiAnnotation )
.forEach( a -> handleMappingAnnotation( a, problemMap ) );
}
+ else if ( IGNORED_ANNOTATION_FQN.equals( qualifiedName ) ) {
+ handleIgnoredAnnotation( psiAnnotation, problemMap );
+ }
+ else if ( IGNORED_LIST_ANNOTATION_FQN.equals( qualifiedName ) ) {
+ extractRepeatableAnnotations( psiAnnotation )
+ .forEach( a -> handleIgnoredAnnotation( a, problemMap ) );
+ }
else {
// Handle annotations containing at least one Mapping annotation
handleAnnotationWithMappingAnnotation( psiAnnotation, problemMap );
@@ -155,6 +165,26 @@ private static void handleMappingAnnotation(PsiAnnotation psiAnnotation,
}
}
+ private static void handleIgnoredAnnotation(PsiAnnotation psiAnnotation,
+ Map> problemMap) {
+ String prefixDot = TargetUtils.getIgnoredPrefix( psiAnnotation );
+
+ PsiAnnotationMemberValue targetsValue = psiAnnotation.findDeclaredAttributeValue( "targets" );
+ for ( PsiAnnotationMemberValue value : AnnotationUtil.arrayAttributeValues( targetsValue ) ) {
+ addIgnoredTarget( value, prefixDot, problemMap );
+ }
+
+ }
+
+ private static void addIgnoredTarget(PsiAnnotationMemberValue value, String prefixDot,
+ Map> problemMap) {
+ String target = getStringAttributeValue( value );
+ if ( target != null && !target.isEmpty() ) {
+ problemMap.computeIfAbsent( prefixDot + target, k -> new ArrayList<>() )
+ .add( value );
+ }
+ }
+
private static class ChangeTargetQuickFix extends LocalQuickFixOnPsiElement {
private final String myText;
diff --git a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
index bc61b1d..80fd1cc 100644
--- a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
+++ b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
@@ -46,6 +46,7 @@
import static org.mapstruct.intellij.util.SourceUtils.findAllSourceProperties;
import static org.mapstruct.intellij.util.SourceUtils.getGenericTypes;
import static org.mapstruct.intellij.util.TargetUtils.findAllDefinedMappingTargets;
+import static org.mapstruct.intellij.util.TargetUtils.findAllIgnoredTargets;
import static org.mapstruct.intellij.util.TargetUtils.findAllSourcePropertiesForCurrentTarget;
import static org.mapstruct.intellij.util.TargetUtils.findAllTargetProperties;
import static org.mapstruct.intellij.util.TargetUtils.getTargetType;
@@ -117,6 +118,12 @@ public void visitMethod(PsiMethod method) {
.collect( Collectors.toSet() );
allTargetProperties.removeAll( definedTargets );
+ // find and remove all @Ignored targets
+ Set ignoredTargets = findAllIgnoredTargets( method )
+ .map( MyJavaElementVisitor::getBaseTarget )
+ .collect( Collectors.toSet() );
+ allTargetProperties.removeAll( ignoredTargets );
+
// find and remove all inherited target properties
Set inheritedTargetProperties = findInheritedTargetProperties( method, mapStructVersion )
.map( MyJavaElementVisitor::getBaseTarget )
diff --git a/src/main/java/org/mapstruct/intellij/util/MapStructVersion.java b/src/main/java/org/mapstruct/intellij/util/MapStructVersion.java
index 77e69b5..f446a45 100644
--- a/src/main/java/org/mapstruct/intellij/util/MapStructVersion.java
+++ b/src/main/java/org/mapstruct/intellij/util/MapStructVersion.java
@@ -12,7 +12,8 @@ public enum MapStructVersion {
V1_2_O( false, false ),
V1_3_O( true, false ),
- V1_4_O( true, true );
+ V1_4_O( true, true ),
+ V1_7_O( true, true );
private final boolean builderSupported;
private final boolean constructorSupported;
diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java b/src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java
index bff2730..43eb11d 100644
--- a/src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java
+++ b/src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java
@@ -298,26 +298,26 @@ private static Stream findAllDefinedMappingAnnotations(@NotNull P
boolean includeMetaAnnotations) {
//TODO cache
PsiAnnotation mappings = findAnnotation( owner, true, MapstructUtil.MAPPINGS_ANNOTATION_FQN );
- Stream mappingsAnnotations = extractMappingAnnotationsFromMappings( mappings );
+ Stream mappingsAnnotations = extractRepeatableAnnotations( mappings );
Stream mappingAnnotations = findMappingAnnotations( owner, includeMetaAnnotations );
return Stream.concat( mappingAnnotations, mappingsAnnotations );
}
@NotNull
- public static Stream extractMappingAnnotationsFromMappings(@Nullable PsiAnnotation mappings) {
- if (mappings == null) {
+ public static Stream extractRepeatableAnnotations(@Nullable PsiAnnotation annotation) {
+ if ( annotation == null ) {
return Stream.empty();
}
//TODO maybe there is a better way to do this, but currently I don't have that much knowledge
- PsiAnnotationMemberValue mappingsValue = mappings.findDeclaredAttributeValue( null );
- if ( mappingsValue instanceof PsiArrayInitializerMemberValue mappingsArrayInitializerMemberValue) {
- return Stream.of( mappingsArrayInitializerMemberValue
+ PsiAnnotationMemberValue value = annotation.findDeclaredAttributeValue( null );
+ if ( value instanceof PsiArrayInitializerMemberValue arrayInitializerMemberValue ) {
+ return Stream.of( arrayInitializerMemberValue
.getInitializers() )
- .filter( MapstructAnnotationUtils::isMappingPsiAnnotation )
+ .filter( PsiAnnotation.class::isInstance )
.map( PsiAnnotation.class::cast );
}
- else if ( mappingsValue instanceof PsiAnnotation mappingsAnnotation ) {
+ else if ( value instanceof PsiAnnotation mappingsAnnotation ) {
return Stream.of( mappingsAnnotation );
}
return Stream.empty();
@@ -396,17 +396,6 @@ private static Stream findValueMappingAnnotations(@NotNull PsiMet
.filter( MapstructAnnotationUtils::isValueMappingAnnotation );
}
- /**
- * @param memberValue that needs to be checked
- *
- * @return {@code true} if the {@code memberValue} is the {@link org.mapstruct.Mapping} {@link PsiAnnotation},
- * {@code false} otherwise
- */
- private static boolean isMappingPsiAnnotation(PsiAnnotationMemberValue memberValue) {
- return memberValue instanceof PsiAnnotation
- && isMappingAnnotation( (PsiAnnotation) memberValue );
- }
-
/**
* @param memberValue that needs to be checked
*
diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java b/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java
index 7d041c2..1e06785 100644
--- a/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java
+++ b/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java
@@ -42,6 +42,15 @@ public static PsiJavaElementPattern.Capture mappingElementPattern(St
return elementPattern( parameterName, MapstructUtil.MAPPING_ANNOTATION_FQN );
}
+ /**
+ * @param parameterName the name of the parameter in the {@code @Ignored} annotation
+ *
+ * @return an element pattern for a parameter in the {@code @Ignored} annotation
+ */
+ public static PsiJavaElementPattern.Capture ignoredElementPattern(String parameterName) {
+ return elementPattern( parameterName, MapstructUtil.IGNORED_ANNOTATION_FQN );
+ }
+
/**
* @param parameterName the name of the parameter in the {@code @Mapper} annotation
*
diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructKotlinElementUtils.java b/src/main/java/org/mapstruct/intellij/util/MapstructKotlinElementUtils.java
index 0fa680e..ce35208 100644
--- a/src/main/java/org/mapstruct/intellij/util/MapstructKotlinElementUtils.java
+++ b/src/main/java/org/mapstruct/intellij/util/MapstructKotlinElementUtils.java
@@ -62,6 +62,19 @@ public static KotlinElementPattern.Capture extends PsiElement> beanMappingElem
);
}
+ /**
+ * @param parameterName the name of the parameter in the {@code @Ignored} annotation
+ *
+ * @return an element pattern for a parameter in the {@code @Ignored} annotation
+ */
+ public static KotlinElementPattern.Capture extends PsiElement> ignoredElementPattern(String parameterName) {
+ return elementPattern(
+ parameterName,
+ MapstructUtil.IGNORED_ANNOTATION_FQN,
+ MapstructUtil.IGNORED_LIST_ANNOTATION_FQN
+ );
+ }
+
private static KotlinElementPattern.Capture extends PsiElement> elementPattern(String parameterName,
String annotationFQN,
String annotationHolderFQN
diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java b/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java
index b0ac84c..7d49019 100644
--- a/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java
+++ b/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java
@@ -53,6 +53,8 @@
import org.mapstruct.Builder;
import org.mapstruct.Context;
import org.mapstruct.EnumMapping;
+import org.mapstruct.Ignored;
+import org.mapstruct.IgnoredList;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
@@ -98,6 +100,9 @@ public class MapstructUtil {
public static final String MAPPINGS_ANNOTATION_FQN = Mappings.class.getName();
+ public static final String IGNORED_ANNOTATION_FQN = Ignored.class.getName();
+ public static final String IGNORED_LIST_ANNOTATION_FQN = IgnoredList.class.getName();
+
static final String VALUE_MAPPING_ANNOTATION_FQN = ValueMapping.class.getName();
static final String VALUE_MAPPINGS_ANNOTATION_FQN = ValueMappings.class.getName();
private static final String MAPPING_TARGET_ANNOTATION_FQN = MappingTarget.class.getName();
@@ -567,12 +572,16 @@ public static MapStructVersion resolveMapStructProjectVersion(@NotNull PsiFile p
}
return CachedValuesManager.getManager( module.getProject() ).getCachedValue( module, () -> {
MapStructVersion mapStructVersion;
- if ( JavaPsiFacade.getInstance( module.getProject() )
- .findClass( ENUM_MAPPING_ANNOTATION_FQN, module.getModuleRuntimeScope( false ) ) != null ) {
+ JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance( module.getProject() );
+ GlobalSearchScope moduleRuntimeScope = module.getModuleRuntimeScope( false );
+ if ( javaPsiFacade.findClass( IGNORED_ANNOTATION_FQN, moduleRuntimeScope ) != null ) {
+ mapStructVersion = MapStructVersion.V1_7_O;
+ }
+ else if ( javaPsiFacade.findClass( ENUM_MAPPING_ANNOTATION_FQN, moduleRuntimeScope ) != null ) {
mapStructVersion = MapStructVersion.V1_4_O;
}
- else if ( JavaPsiFacade.getInstance( module.getProject() )
- .findClass( BUILDER_ANNOTATION_FQN, module.getModuleRuntimeScope( false ) ) != null ) {
+ else if ( javaPsiFacade
+ .findClass( BUILDER_ANNOTATION_FQN, moduleRuntimeScope ) != null ) {
mapStructVersion = MapStructVersion.V1_3_O;
}
else {
diff --git a/src/main/java/org/mapstruct/intellij/util/TargetUtils.java b/src/main/java/org/mapstruct/intellij/util/TargetUtils.java
index 886ca17..7fbb60d 100644
--- a/src/main/java/org/mapstruct/intellij/util/TargetUtils.java
+++ b/src/main/java/org/mapstruct/intellij/util/TargetUtils.java
@@ -430,6 +430,58 @@ public static Stream findAllDefinedMappingTargets(@NotNull PsiModifierLi
.filter( s -> !s.isEmpty() );
}
+ /**
+ * Find all target properties that are ignored via {@link org.mapstruct.Ignored} annotations for the given method.
+ * This considers both directly placed {@code @Ignored} annotations and those wrapped in {@code @IgnoredList},
+ * and prepends the {@code prefix} attribute value (if any) to each target.
+ *
+ * @param method that needs to be checked
+ *
+ * @return a stream of all ignored target property names (with prefix applied)
+ */
+ @NotNull
+ public static Stream findAllIgnoredTargets(@NotNull PsiMethod method) {
+ Stream ignoredAnnotations = findIgnoredAnnotations( method );
+ return ignoredAnnotations.flatMap( TargetUtils::extractIgnoredTargets );
+ }
+
+ @NotNull
+ private static Stream findIgnoredAnnotations(@NotNull PsiMethod method) {
+ PsiAnnotation ignoredList = AnnotationUtil.findAnnotation(
+ method,
+ true,
+ MapstructUtil.IGNORED_LIST_ANNOTATION_FQN
+ );
+
+ Stream fromList = MapstructAnnotationUtils.extractRepeatableAnnotations( ignoredList );
+
+ Stream direct = Stream.of( method.getModifierList().getAnnotations() )
+ .filter( a -> MapstructUtil.IGNORED_ANNOTATION_FQN.equals( a.getQualifiedName() ) );
+
+ return Stream.concat( direct, fromList );
+ }
+
+ @NotNull
+ private static Stream extractIgnoredTargets(@NotNull PsiAnnotation ignoredAnnotation) {
+
+ String prefixDot = getIgnoredPrefix( ignoredAnnotation );
+
+ return AnnotationUtil.arrayAttributeValues( ignoredAnnotation.findDeclaredAttributeValue( "targets" ) )
+ .stream()
+ .map( AnnotationUtil::getStringAttributeValue )
+ .filter( Objects::nonNull )
+ .filter( s -> !s.isEmpty() )
+ .map( target -> prefixDot + target );
+ }
+
+ public static String getIgnoredPrefix(@Nullable PsiAnnotation ignoredAnnotation) {
+ if ( ignoredAnnotation == null ) {
+ return "";
+ }
+ String prefix = AnnotationUtil.getDeclaredStringAttributeValue( ignoredAnnotation, "prefix" );
+ return ( prefix != null && !prefix.isEmpty() ) ? prefix + "." : "";
+ }
+
/**
* Find all implicit source properties for all targets mapping to the current target, i.e. ".".
*
diff --git a/src/main/java/org/mapstruct/intellij/util/patterns/KotlinElementPattern.java b/src/main/java/org/mapstruct/intellij/util/patterns/KotlinElementPattern.java
index 9c2a111..8ac0426 100644
--- a/src/main/java/org/mapstruct/intellij/util/patterns/KotlinElementPattern.java
+++ b/src/main/java/org/mapstruct/intellij/util/patterns/KotlinElementPattern.java
@@ -37,12 +37,14 @@ public Self insideRepeatableAnnotationParam(
KtValueArgumentPattern ktValueArgumentPattern = ktValueArgument().withName( parameterName );
return withElementType( KtStubElementTypes.STRING_TEMPLATE ).andOr(
- withParent(
+ withAncestor(
+ 2,
ktValueArgumentPattern
.withAncestor( 5, ktAnnotation().qName( annotationHolderQualifiedName ) )
),
- withParent(
+ withAncestor(
+ 2,
ktValueArgumentPattern
.withSuperParent( 2, ktAnnotation().qName( annotationQualifiedName ) )
)
diff --git a/src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java b/src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java
index 093b70d..7b6446a 100644
--- a/src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java
+++ b/src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java
@@ -271,6 +271,20 @@ public void testCarMapperReturnTargetCarDto() {
assertCarDtoAutoComplete();
}
+ public void testCarMapperReturnTargetCarDtoWithIgnored() {
+ configureByTestName();
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+
public void testCarMapperReturnTargetFluentCarDto() {
configureByTestName();
assertCarDtoAutoComplete();
diff --git a/src/test/java/org/mapstruct/intellij/completion/IgnoredTargetsCompletionTestCase.java b/src/test/java/org/mapstruct/intellij/completion/IgnoredTargetsCompletionTestCase.java
new file mode 100644
index 0000000..bd14a1a
--- /dev/null
+++ b/src/test/java/org/mapstruct/intellij/completion/IgnoredTargetsCompletionTestCase.java
@@ -0,0 +1,209 @@
+/*
+ * 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.completion;
+
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiReference;
+import org.mapstruct.intellij.MapstructBaseCompletionTestCase;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Filip Hrisafov
+ */
+public class IgnoredTargetsCompletionTestCase extends MapstructBaseCompletionTestCase {
+
+ @Override
+ protected String getTestDataPath() {
+ return "testData/completion/ignored";
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ addDirectoryToProject( "../../mapping/dto" );
+ }
+
+ private void assertCarDtoAutoComplete() {
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "make",
+ "seatCount",
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+
+ public void testIgnoredTargetsNoPrefix() {
+ configureByTestName();
+ assertCarDtoAutoComplete();
+ }
+
+ public void testIgnoredTargetsWithPrefix() {
+ configureByTestName();
+ assertCarDtoAutoComplete();
+ }
+
+ public void testIgnoredTargetsMultipleTargets() {
+ configureByTestName();
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+
+ public void testIgnoredTargetsWithPrefixMultipleTargets() {
+ configureByTestName();
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "seatCount",
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+
+ public void testIgnoredTargetsWithMapping() {
+ configureByTestName();
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+
+ public void testIgnoredTargetsWithPrefixAndMapping() {
+ configureByTestName();
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+
+ public void testIgnoredTargetsSingleNoArray() {
+ configureByTestName();
+ assertCarDtoAutoComplete();
+ }
+
+ public void testIgnoredTargetsReferenceTarget() {
+ myFixture.configureByFile( "IgnoredTargetsReferenceTarget.java" );
+ PsiElement reference = myFixture.getElementAtCaret();
+
+ assertThat( reference )
+ .isInstanceOfSatisfying( PsiMethod.class, method -> {
+ assertThat( method.getName() ).isEqualTo( "setSeatCount" );
+ } );
+ }
+
+ public void testIgnoredTargetsReferenceTargetWithPrefix() {
+ myFixture.configureByFile( "IgnoredTargetsReferenceTargetWithPrefix.java" );
+ PsiElement reference = myFixture.getElementAtCaret();
+
+ assertThat( reference )
+ .isInstanceOfSatisfying( PsiMethod.class, method -> {
+ assertThat( method.getName() ).isEqualTo( "setMake" );
+ } );
+ }
+
+ public void testIgnoredTargetsReferenceUnknownTarget() {
+ myFixture.configureByFile( "IgnoredTargetsReferenceUnknownTarget.java" );
+ PsiReference reference = myFixture.getFile().findReferenceAt( myFixture.getCaretOffset() );
+ assertThat( reference ).isNotNull();
+ assertThat( reference.resolve() ).isNull();
+ }
+
+ public void testIgnoredTargetsReferenceNestedTarget() {
+ myFixture.configureByFile( "IgnoredTargetsReferenceNestedTarget.java" );
+ PsiElement reference = myFixture.getElementAtCaret();
+
+ assertThat( reference )
+ .isInstanceOfSatisfying( PsiMethod.class, method -> {
+ assertThat( method.getName() ).isEqualTo( "setName" );
+ } );
+ }
+
+ public void testIgnoredTargetsWithInvalidPrefix() {
+ myFixture.configureByFile( "IgnoredTargetsWithInvalidPrefix.java" );
+ complete();
+ assertThat( myItems ).isEmpty();
+ }
+
+ public void testIgnoredTargetsMultipleAnnotations() {
+ configureByTestName();
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+
+ public void testIgnoredTargetsWithIgnoredList() {
+ configureByTestName();
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+
+ public void testIgnoredTargetsNoPrefixKotlin() {
+ configureByFile( "/" + getTestName( false ) + ".kt" );
+ assertCarDtoAutoComplete();
+ }
+
+ public void testIgnoredTargetsWithIgnoredListKotlin() {
+ configureByFile( "/" + getTestName( false ) + ".kt" );
+ assertThat( myItems )
+ .extracting( LookupElement::getLookupString )
+ .containsExactlyInAnyOrder(
+ "manufacturingYear",
+ "myDriver",
+ "passengers",
+ "price",
+ "category",
+ "available"
+ );
+ }
+}
diff --git a/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java
index 9770355..0320985 100644
--- a/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java
+++ b/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java
@@ -49,4 +49,8 @@ public void testUnknownIgnoreUnmappedSourceReference() {
public void testUnknownQualifiedByNameReferenceReference() {
doTest();
}
+
+ public void testUnknownIgnoredTargetReference() {
+ doTest();
+ }
}
diff --git a/src/test/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspectionTest.java
index 486f524..c7d13db 100644
--- a/src/test/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspectionTest.java
+++ b/src/test/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspectionTest.java
@@ -24,6 +24,14 @@ public class TargetPropertyMappedMoreThanOnceInspectionTest extends BaseInspecti
return TargetPropertyMappedMoreThanOnceInspection.class;
}
+ public void testTargetPropertyMappedMoreThanOnceWithIgnored() {
+ doTest();
+ }
+
+ public void testTargetPropertyMappedMoreThanOnceWithIgnoredList() {
+ doTest();
+ }
+
public void testTargetPropertyMappedMoreThanOnce() {
doTest();
String testName = getTestName( false );
diff --git a/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithIgnoredInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithIgnoredInspectionTest.java
new file mode 100644
index 0000000..41f6098
--- /dev/null
+++ b/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithIgnoredInspectionTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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 java.util.List;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import org.jetbrains.annotations.NotNull;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Filip Hrisafov
+ */
+public class UnmappedTargetPropertiesWithIgnoredInspectionTest extends BaseInspectionTest {
+
+ @NotNull
+ @Override
+ protected Class getInspection() {
+ return UnmappedTargetPropertiesInspection.class;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ myFixture.copyFileToProject(
+ "UnmappedTargetPropertiesData.java",
+ "org/example/data/UnmappedTargetPropertiesData.java"
+ );
+ }
+
+ public void testUnmappedTargetPropertiesWithIgnored() {
+ doTest();
+ List allQuickFixes = myFixture.getAllQuickFixes();
+
+ assertThat( allQuickFixes )
+ .extracting( IntentionAction::getText )
+ .as( "Intent Text" )
+ .containsExactly(
+ "Ignore unmapped target property: 'moreTarget'",
+ "Add unmapped target property: 'moreTarget'"
+ );
+ }
+}
diff --git a/testData/completion/ignored/IgnoredTargetsMultipleAnnotations.java b/testData/completion/ignored/IgnoredTargetsMultipleAnnotations.java
new file mode 100644
index 0000000..9bc1106
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsMultipleAnnotations.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsMultipleAnnotations {
+
+ @Ignored(targets = { "make", "seatCount" })
+ @Ignored(targets = { "" })
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsMultipleTargets.java b/testData/completion/ignored/IgnoredTargetsMultipleTargets.java
new file mode 100644
index 0000000..86abb91
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsMultipleTargets.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsMultipleTargets {
+
+ @Ignored(targets = { "make", "seatCount", "" })
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsNoPrefix.java b/testData/completion/ignored/IgnoredTargetsNoPrefix.java
new file mode 100644
index 0000000..a831029
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsNoPrefix.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsNoPrefix {
+
+ @Ignored(targets = { "" })
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsNoPrefixKotlin.kt b/testData/completion/ignored/IgnoredTargetsNoPrefixKotlin.kt
new file mode 100644
index 0000000..ed4377e
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsNoPrefixKotlin.kt
@@ -0,0 +1,18 @@
+/**
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex
+
+import org.mapstruct.Ignored
+import org.mapstruct.Mapper
+import org.example.dto.Car
+import org.example.dto.CarDtoKt
+
+@Mapper
+interface IgnoredTargetsNoPrefixKotlin {
+
+ @Ignored(targets = [""])
+ fun carToCarDto(car: Car): CarDtoKt
+}
diff --git a/testData/completion/ignored/IgnoredTargetsReferenceNestedTarget.java b/testData/completion/ignored/IgnoredTargetsReferenceNestedTarget.java
new file mode 100644
index 0000000..71bc8ea
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsReferenceNestedTarget.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsReferenceNestedTarget {
+
+ @Ignored(targets = { "myDriver.name" })
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsReferenceTarget.java b/testData/completion/ignored/IgnoredTargetsReferenceTarget.java
new file mode 100644
index 0000000..b60cccf
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsReferenceTarget.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsReferenceTarget {
+
+ @Ignored(targets = { "seatCount" })
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsReferenceTargetWithPrefix.java b/testData/completion/ignored/IgnoredTargetsReferenceTargetWithPrefix.java
new file mode 100644
index 0000000..24bc1b6
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsReferenceTargetWithPrefix.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Garage;
+import org.example.dto.GarageDto;
+
+@Mapper
+public interface IgnoredTargetsReferenceTargetWithPrefix {
+
+ @Ignored(prefix = "car", targets = { "make" })
+ GarageDto garageToGarageDto(Garage garage);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsReferenceUnknownTarget.java b/testData/completion/ignored/IgnoredTargetsReferenceUnknownTarget.java
new file mode 100644
index 0000000..d8e91a7
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsReferenceUnknownTarget.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsReferenceUnknownTarget {
+
+ @Ignored(targets = { "unknown" })
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsSingleNoArray.java b/testData/completion/ignored/IgnoredTargetsSingleNoArray.java
new file mode 100644
index 0000000..22ccaa6
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsSingleNoArray.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsSingleNoArray {
+
+ @Ignored(targets = "")
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsWithIgnoredList.java b/testData/completion/ignored/IgnoredTargetsWithIgnoredList.java
new file mode 100644
index 0000000..ae5fb88
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsWithIgnoredList.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.IgnoredList;
+import org.mapstruct.Mapper;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsWithIgnoredList {
+
+ @IgnoredList({
+ @Ignored(targets = { "make", "seatCount" }),
+ @Ignored(targets = { "" })
+ })
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsWithIgnoredListKotlin.kt b/testData/completion/ignored/IgnoredTargetsWithIgnoredListKotlin.kt
new file mode 100644
index 0000000..2281f47
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsWithIgnoredListKotlin.kt
@@ -0,0 +1,22 @@
+/**
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex
+
+import org.mapstruct.Ignored
+import org.mapstruct.IgnoredList
+import org.mapstruct.Mapper
+import org.example.dto.Car
+import org.example.dto.CarDtoKt
+
+@Mapper
+interface IgnoredTargetsWithIgnoredListKotlin {
+
+ @IgnoredList(
+ Ignored(targets = ["make", "seatCount"]),
+ Ignored(targets = [""])
+ )
+ fun carToCarDto(car: Car): CarDtoKt
+}
diff --git a/testData/completion/ignored/IgnoredTargetsWithInvalidPrefix.java b/testData/completion/ignored/IgnoredTargetsWithInvalidPrefix.java
new file mode 100644
index 0000000..d804225
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsWithInvalidPrefix.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Garage;
+import org.example.dto.GarageDto;
+
+@Mapper
+public interface IgnoredTargetsWithInvalidPrefix {
+
+ @Ignored(prefix = "nonExistent", targets = { "" })
+ GarageDto garageToGarageDto(Garage garage);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsWithMapping.java b/testData/completion/ignored/IgnoredTargetsWithMapping.java
new file mode 100644
index 0000000..f6e60f9
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsWithMapping.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.example.dto.Car;
+import org.example.dto.CarDto;
+
+@Mapper
+public interface IgnoredTargetsWithMapping {
+
+ @Ignored(targets = { "seatCount", "" })
+ @Mapping(target = "make", ignore = true)
+ CarDto carToCarDto(Car car);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsWithPrefix.java b/testData/completion/ignored/IgnoredTargetsWithPrefix.java
new file mode 100644
index 0000000..c65389a
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsWithPrefix.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Garage;
+import org.example.dto.GarageDto;
+
+@Mapper
+public interface IgnoredTargetsWithPrefix {
+
+ @Ignored(prefix = "car", targets = { "" })
+ GarageDto garageToGarageDto(Garage garage);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsWithPrefixAndMapping.java b/testData/completion/ignored/IgnoredTargetsWithPrefixAndMapping.java
new file mode 100644
index 0000000..30fb96b
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsWithPrefixAndMapping.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.example.dto.Garage;
+import org.example.dto.GarageDto;
+
+@Mapper
+public interface IgnoredTargetsWithPrefixAndMapping {
+
+ @Ignored(prefix = "car", targets = { "seatCount", "" })
+ @Mapping(target = "car.make", source = "car.make")
+ GarageDto garageToGarageDto(Garage garage);
+}
diff --git a/testData/completion/ignored/IgnoredTargetsWithPrefixMultipleTargets.java b/testData/completion/ignored/IgnoredTargetsWithPrefixMultipleTargets.java
new file mode 100644
index 0000000..5e6a637
--- /dev/null
+++ b/testData/completion/ignored/IgnoredTargetsWithPrefixMultipleTargets.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.example.dto.Garage;
+import org.example.dto.GarageDto;
+
+@Mapper
+public interface IgnoredTargetsWithPrefixMultipleTargets {
+
+ @Ignored(prefix = "car", targets = { "make", "" })
+ GarageDto garageToGarageDto(Garage garage);
+}
diff --git a/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnored.java b/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnored.java
new file mode 100644
index 0000000..7c2ba0f
--- /dev/null
+++ b/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnored.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+class Source {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+class Target {
+ private String testName;
+
+ public String getTestName() {
+ return testName;
+ }
+
+ public void setTestName(String testName) {
+ this.testName = testName;
+ }
+}
+
+class Inner {
+
+ private Target target;
+
+ public Target getTarget() {
+ return target;
+ }
+
+ public void setTarget(Target target) {
+ this.target = target;
+ }
+}
+
+@Mapper
+interface MappingAndIgnoredSameTargetMapper {
+
+ @Mapping(target = "testName", source = "name")
+ @Ignored(targets = { "testName" })
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredAndIgnoredSameTargetMapper {
+
+ @Ignored(targets = { "testName" })
+ @Ignored(targets = { "testName" })
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredWithPrefixAndMappingSameTargetMapper {
+
+ @Mapping(target = "target.testName", source = "name")
+ @Ignored(prefix = "target", targets = { "testName" })
+ Inner map(Source source);
+}
+
+@Mapper
+interface IgnoredNoConflictMapper {
+
+ @Mapping(target = "testName", source = "name")
+ @Ignored(targets = { "other" })
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredWithPrefixNoConflictMapper {
+
+ @Mapping(target = "testName", source = "name")
+ @Ignored(prefix = "target", targets = { "testName" })
+ Inner map(Source source);
+}
+
+@Mapper
+interface IgnoredSingleValueAndMappingMapper {
+
+ @Mapping(target = "testName", source = "name")
+ @Ignored(targets = "testName")
+ Target map(Source source);
+}
+
+@Mapper
+interface MultipleTargetsInIgnoredMapper {
+
+ @Mapping(target = "testName", source = "name")
+ @Ignored(targets = { "testName", "other" })
+ Target map(Source source);
+}
diff --git a/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnoredList.java b/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnoredList.java
new file mode 100644
index 0000000..fd51de2
--- /dev/null
+++ b/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnoredList.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Ignored;
+import org.mapstruct.IgnoredList;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+class Source {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+class Target {
+ private String testName;
+ private String moreTarget;
+
+ public String getTestName() {
+ return testName;
+ }
+
+ public void setTestName(String testName) {
+ this.testName = testName;
+ }
+
+ public String getMoreTarget() {
+ return moreTarget;
+ }
+
+ public void setMoreTarget(String moreTarget) {
+ this.moreTarget = moreTarget;
+ }
+}
+
+@Mapper
+interface MappingAndIgnoredListSameTargetMapper {
+
+ @Mapping(target = "testName", source = "name")
+ @IgnoredList({
+ @Ignored(targets = { "testName" })
+ })
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredListDuplicateTargetMapper {
+
+ @IgnoredList({
+ @Ignored(targets = { "testName" }),
+ @Ignored(targets = { "testName" })
+ })
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredListNoConflictMapper {
+
+ @IgnoredList({
+ @Ignored(targets = { "testName" }),
+ @Ignored(targets = { "moreTarget" })
+ })
+ Target map(Source source);
+}
diff --git a/testData/inspection/UnknownIgnoredTargetReference.java b/testData/inspection/UnknownIgnoredTargetReference.java
new file mode 100644
index 0000000..20ca865
--- /dev/null
+++ b/testData/inspection/UnknownIgnoredTargetReference.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+
+class Source {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+class Target {
+
+ private String testName;
+
+ public String getTestName() {
+ return testName;
+ }
+
+ public void setTestName(String testName) {
+ this.testName = testName;
+ }
+}
+
+class Inner {
+
+ private Target target;
+
+ public Target getTarget() {
+ return target;
+ }
+
+ public void setTarget(Target target) {
+ this.target = target;
+ }
+}
+
+@Mapper
+interface SingleIgnoredMapper {
+
+ @Ignored(targets = { "name" })
+ Target map(Source source);
+}
+
+@Mapper
+interface ValidIgnoredMapper {
+
+ @Ignored(targets = { "testName" })
+ Target map(Source source);
+}
+
+@Mapper
+interface EmptyIgnoredMapper {
+
+ @Ignored(targets = { "" })
+ Target map(Source source);
+}
+
+@Mapper
+interface MultipleIgnoredMapper {
+
+ @Ignored(targets = { "testName", "unknown" })
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredWithPrefixMapper {
+
+ @Ignored(prefix = "target", targets = { "unknown" })
+ Inner map(Source source);
+}
+
+@Mapper
+interface ValidIgnoredWithPrefixMapper {
+
+ @Ignored(prefix = "target", targets = { "testName" })
+ Inner map(Source source);
+}
+
+@Mapper
+interface IgnoredNestedTargetMapper {
+
+ @Ignored(targets = { "target.unknown" })
+ Inner map(Source source);
+}
+
+@Mapper
+interface ValidIgnoredNestedTargetMapper {
+
+ @Ignored(targets = { "target.testName" })
+ Inner map(Source source);
+}
+
+@Mapper
+interface SingleNoArrayIgnoredMapper {
+
+ @Ignored(targets = "name")
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredWithInvalidPrefixMapper {
+
+ @Ignored(prefix = "nonExistent", targets = { "foo" })
+ Inner map(Source source);
+}
diff --git a/testData/inspection/UnmappedTargetPropertiesWithIgnored.java b/testData/inspection/UnmappedTargetPropertiesWithIgnored.java
new file mode 100644
index 0000000..afa9104
--- /dev/null
+++ b/testData/inspection/UnmappedTargetPropertiesWithIgnored.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.example.data.UnmappedTargetPropertiesData.Target;
+import org.example.data.UnmappedTargetPropertiesData.Source;
+import org.example.data.UnmappedTargetPropertiesData.TargetWithInnerObject;
+
+@Mapper
+interface AllIgnoredMapper {
+
+ @Ignored(targets = { "testName", "moreTarget" })
+ Target map(Source source);
+}
+
+@Mapper
+interface PartiallyIgnoredMapper {
+
+ @Ignored(targets = { "testName" })
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredAndMappedMapper {
+
+ @Ignored(targets = { "moreTarget" })
+ @Mapping(target = "testName", source = "name")
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredWithPrefixMapper {
+
+ @Ignored(prefix = "testTarget", targets = { "testName", "moreTarget" })
+ TargetWithInnerObject map(Source source);
+}
+
+@Mapper
+interface MultipleIgnoredAnnotationsMapper {
+
+ @Ignored(targets = { "testName" })
+ @Ignored(targets = { "moreTarget" })
+ Target map(Source source);
+}
+
+@Mapper
+interface IgnoredWithPrefixAndMappingMapper {
+
+ @Ignored(prefix = "testTarget", targets = { "moreTarget" })
+ @Mapping(target = "testTarget.testName", source = "name")
+ TargetWithInnerObject map(Source source);
+}
diff --git a/testData/mapping/CarMapperReturnTargetCarDtoWithIgnored.java b/testData/mapping/CarMapperReturnTargetCarDtoWithIgnored.java
new file mode 100644
index 0000000..13ff967
--- /dev/null
+++ b/testData/mapping/CarMapperReturnTargetCarDtoWithIgnored.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.complex;
+
+import org.mapstruct.Ignored;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.example.dto.CarDto;
+import org.example.dto.Car;
+
+@Mapper
+public interface CarMapperReturnTargetCarDtoWithIgnored {
+
+ @Ignored(targets = { "seatCount", "make" })
+ @Mapping(source = "manufacturingDate", target = "")
+ CarDto carToCarDto(Car car);
+}