From f7c5df01495666a8be6a2503f60b0ba188335af7 Mon Sep 17 00:00:00 2001 From: thunderhook <8238759+thunderhook@users.noreply.github.com> Date: Sun, 5 Oct 2025 01:12:14 +0200 Subject: [PATCH] #243: Do not warn for unmapped source properties on maps --- .../codeinsight/references/BaseReference.java | 2 +- .../references/BaseValueMappingReference.java | 2 +- .../MapstructReferenceInspection.java | 48 ++++++++++++++++--- ...ableSourcePropertyInspectionOnMapTest.java | 31 ++++++++++++ ...ableSourcePropertyInspectionOnMapTest.java | 28 +++++++++++ 5 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 src/test/java/org/mapstruct/intellij/bugs/_243/DisableSourcePropertyInspectionOnMapTest.java create mode 100644 testData/bugs/_243/DisableSourcePropertyInspectionOnMapTest.java diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseReference.java index fc0dc97..0f00328 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseReference.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseReference.java @@ -43,7 +43,7 @@ public abstract class BaseReference extends PsiReferenceBase impleme * @return The mapping method that this reference belongs to */ @Nullable - PsiMethod getMappingMethod() { + public PsiMethod getMappingMethod() { PsiElement element = getElement(); UExpression expression = UastContextKt.toUElement( element, UExpression.class ); if ( expression != null ) { diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseValueMappingReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseValueMappingReference.java index c8f7129..f129eed 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseValueMappingReference.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseValueMappingReference.java @@ -47,7 +47,7 @@ public final PsiElement resolve() { @Override @Nullable - PsiMethod getMappingMethod() { + public PsiMethod getMappingMethod() { PsiMethod mappingMethod = super.getMappingMethod(); if ( isNotValueMapping( mappingMethod ) ) { return null; diff --git a/src/main/java/org/mapstruct/intellij/inspection/MapstructReferenceInspection.java b/src/main/java/org/mapstruct/intellij/inspection/MapstructReferenceInspection.java index e8dd873..0b3f083 100644 --- a/src/main/java/org/mapstruct/intellij/inspection/MapstructReferenceInspection.java +++ b/src/main/java/org/mapstruct/intellij/inspection/MapstructReferenceInspection.java @@ -8,27 +8,34 @@ import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.util.TextRange; +import com.intellij.psi.CommonClassNames; import com.intellij.psi.ContributedReferenceHost; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiLanguageInjectionHost; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameter; import com.intellij.psi.PsiReference; +import com.intellij.psi.PsiType; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.mapstruct.intellij.codeinsight.references.BaseReference; import org.mapstruct.intellij.codeinsight.references.BaseValueMappingReference; /** * Inspection that checks if mapstruct references can be resolved. - * @see BaseReference + * * @author hduelme + * @see BaseReference */ public class MapstructReferenceInspection extends InspectionBase { @Override @NotNull PsiElementVisitor buildVisitorInternal(@NotNull ProblemsHolder holder, boolean isOnTheFly) { - return new MapstructReferenceVisitor(holder); + return new MapstructReferenceVisitor( holder ); } private static class MapstructReferenceVisitor extends PsiElementVisitor { @@ -44,14 +51,16 @@ private MapstructReferenceVisitor(ProblemsHolder holder) { */ @Override public void visitElement(@NotNull PsiElement element) { - if (element instanceof ContributedReferenceHost r && element instanceof PsiLanguageInjectionHost) { - for (PsiReference psiReference : r.getReferences()) { + if ( element instanceof ContributedReferenceHost r && element instanceof PsiLanguageInjectionHost ) { + for ( PsiReference psiReference : r.getReferences() ) { if ( psiReference instanceof BaseReference baseReference && psiReference.resolve() == null ) { TextRange range = psiReference.getRangeInElement(); - if (range.isEmpty() && range.getStartOffset() == 1 && "\"\"".equals( element.getText() ) ) { + if ( range.isEmpty() && range.getStartOffset() == 1 && "\"\"".equals( element.getText() ) ) { String message = ProblemsHolder.unresolvedReferenceMessage( baseReference ); - holder.registerProblem( element, message, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, - TextRange.create( 0, 2 ) ); + holder.registerProblem( + element, message, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, + TextRange.create( 0, 2 ) + ); } else if ( shouldRegisterProblem( baseReference ) ) { holder.registerProblem( psiReference ); @@ -67,9 +76,34 @@ private boolean shouldRegisterProblem(BaseReference reference) { return valueMappingReference.getEnumClass() != null; } + if ( singleSourceParameterIsOfTypeMap( reference.getMappingMethod() ) ) { + return false; + } + return !containingClassIsAnnotationType( reference.getElement() ); } + private boolean singleSourceParameterIsOfTypeMap(@Nullable PsiMethod mappingMethod) { + + if ( mappingMethod != null ) { + PsiParameter[] parameters = mappingMethod.getParameterList().getParameters(); + if ( parameters.length > 0 ) { + PsiType parameterType = parameters[0].getType(); + return isMapType( parameterType ); + } + } + + return false; + } + + private boolean isMapType(PsiType type) { + PsiClass psiClass = PsiUtil.resolveClassInType( type ); + if ( psiClass == null ) { + return false; + } + return CommonClassNames.JAVA_UTIL_MAP.equals( psiClass.getQualifiedName() ); + } + private boolean containingClassIsAnnotationType(PsiElement element) { PsiClass containingClass = PsiTreeUtil.getParentOfType( element, PsiClass.class ); diff --git a/src/test/java/org/mapstruct/intellij/bugs/_243/DisableSourcePropertyInspectionOnMapTest.java b/src/test/java/org/mapstruct/intellij/bugs/_243/DisableSourcePropertyInspectionOnMapTest.java new file mode 100644 index 0000000..1eda638 --- /dev/null +++ b/src/test/java/org/mapstruct/intellij/bugs/_243/DisableSourcePropertyInspectionOnMapTest.java @@ -0,0 +1,31 @@ +/* + * 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.bugs._243; + +import org.jetbrains.annotations.NotNull; +import org.mapstruct.intellij.inspection.BaseInspectionTest; +import org.mapstruct.intellij.inspection.MapstructReferenceInspection; + +/** + * @author Oliver Erhart + */ +public class DisableSourcePropertyInspectionOnMapTest extends BaseInspectionTest { + + @Override + protected String getTestDataPath() { + return "testData/bugs/_243"; + } + + @NotNull + @Override + protected Class getInspection() { + return MapstructReferenceInspection.class; + } + + public void testDisableSourcePropertyInspectionOnMapTest() { + doTest(); + } +} diff --git a/testData/bugs/_243/DisableSourcePropertyInspectionOnMapTest.java b/testData/bugs/_243/DisableSourcePropertyInspectionOnMapTest.java new file mode 100644 index 0000000..5bce4de --- /dev/null +++ b/testData/bugs/_243/DisableSourcePropertyInspectionOnMapTest.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 + */ + +import java.time.LocalDate; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper +abstract class Issue243Mapper { + + @Mapping(source = "exDate", target = "exDate", dateFormat = "yyyy-MM-dd") + @Mapping(source = "payDate", target = "payDate", dateFormat = "yyyy-MM-dd") + @Mapping(source = "recordDate", target = "recordDate", dateFormat = "yyyy-MM-dd") + @Mapping(source = "annDate", target = "annDate", dateFormat = "yyyy-MM-dd") + public abstract CorporateAction toCorporateAction(Map rowValues); +} + +class CorporateAction { + public LocalDate exDate; + public LocalDate payDate; + public LocalDate recordDate; + public LocalDate annDate; +}