diff --git a/README.md b/README.md
index 3f44c83..000ea87 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,8 @@ To learn more about MapStruct have a look at the [mapstruct](https://github.com/
* 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.
* `*` 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 `qualifiedByName` in `@Mapping` annotation
## Requirements
diff --git a/description.html b/description.html
index ef0e473..7c30808 100644
--- a/description.html
+++ b/description.html
@@ -43,6 +43,8 @@
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.
*
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 qualifiedByName
in @Mapping
annotation.
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 f6b520b..fcdc738 100644
--- a/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseReference.java
+++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseReference.java
@@ -21,7 +21,7 @@
*
* @author Filip Hrisafov
*/
-abstract class BaseReference extends PsiReferenceBase {
+public abstract class BaseReference extends PsiReferenceBase {
/**
* @param element the element for which a reference should be found
diff --git a/src/main/java/org/mapstruct/intellij/inspection/MapstructReferenceInspection.java b/src/main/java/org/mapstruct/intellij/inspection/MapstructReferenceInspection.java
new file mode 100644
index 0000000..8e5b43e
--- /dev/null
+++ b/src/main/java/org/mapstruct/intellij/inspection/MapstructReferenceInspection.java
@@ -0,0 +1,62 @@
+/*
+ * 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.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.ContributedReferenceHost;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiLanguageInjectionHost;
+import com.intellij.psi.PsiReference;
+import org.jetbrains.annotations.NotNull;
+import org.mapstruct.intellij.codeinsight.references.BaseReference;
+
+/**
+ * Inspection that checks if mapstruct references can be resolved.
+ * @see BaseReference
+ * @author hduelme
+ */
+public class MapstructReferenceInspection extends InspectionBase {
+
+ @Override
+ @NotNull PsiElementVisitor buildVisitorInternal(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new MapstructReferenceVisitor(holder);
+ }
+
+ private static class MapstructReferenceVisitor extends PsiElementVisitor {
+
+ private final ProblemsHolder holder;
+
+ private MapstructReferenceVisitor(ProblemsHolder holder) {
+ this.holder = holder;
+ }
+
+ /**
+ * Based on org.intellij.plugins.intelliLang.references.InjectedReferencesInspection
+ */
+ @Override
+ public void visitElement(@NotNull PsiElement element) {
+ if (element instanceof ContributedReferenceHost r && element instanceof PsiLanguageInjectionHost) {
+ for (PsiReference psiReference : r.getReferences()) {
+ if (psiReference instanceof BaseReference && psiReference.resolve() == null) {
+ TextRange range = psiReference.getRangeInElement();
+ if (range.isEmpty() && range.getStartOffset() == 1 && "\"\"".equals( element.getText() ) ) {
+ String message = ProblemsHolder.unresolvedReferenceMessage( psiReference );
+ holder.registerProblem( element, message, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL,
+ TextRange.create( 0, 2 ) );
+ }
+ else {
+ holder.registerProblem( psiReference );
+ }
+ }
+ }
+ }
+ super.visitElement( element );
+ }
+ }
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index 37cd186..22a4c1c 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -147,6 +147,14 @@
key="inspection.source.property.this.used"
shortName="ThisUsedAsSourcePropertyInspection"
implementationClass="org.mapstruct.intellij.inspection.ThisUsedAsSourcePropertyInspection"/>
+
diff --git a/src/main/resources/inspectionDescriptions/MapstructReferenceInspection.html b/src/main/resources/inspectionDescriptions/MapstructReferenceInspection.html
new file mode 100644
index 0000000..24b9d27
--- /dev/null
+++ b/src/main/resources/inspectionDescriptions/MapstructReferenceInspection.html
@@ -0,0 +1,12 @@
+
+
+This inspection reports unresolved mapstruct references.
+
+@Mapper
+public interface EmployeeMapper {
+ @Mapping(target = "dto", source = "no_exists") // highlighted if source doesn't exist
+ Employee toEmployee(EmployeeDto employeeDto, @Context CycleAvoidingMappingContext context);
+}
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties b/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
index d9311be..06c160a 100644
--- a/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
+++ b/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
@@ -28,6 +28,7 @@ inspection.wrong.map.mapping.map.key.change.to.string=Change key type to String
inspection.target.property.mapped.more.than.once=Target property ''{0}'' must not be mapped more than once.
inspection.target.property.mapped.more.than.once.title=Target properties must not be mapped more than once.
inspection.source.property.this.used=''.'' should not be used as a source.
+inspection.mapstruct.references=Injected mapstruct references
intention.add.ignore.all.unmapped.target.properties=Add ignore all unmapped target properties
intention.add.ignore.unmapped.target.property=Add ignore unmapped target property
intention.add.unmapped.target.property=Add unmapped target property
diff --git a/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java
new file mode 100644
index 0000000..9770355
--- /dev/null
+++ b/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.codeInspection.LocalInspectionTool;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author hduelme
+ */
+public class MapstructReferenceInspectionTest extends BaseInspectionTest {
+
+ @Override
+ protected @NotNull Class extends LocalInspectionTool> getInspection() {
+ return MapstructReferenceInspection.class;
+ }
+
+ public void testUnknownTargetReference() {
+ doTest();
+ }
+
+ public void testUnknownNestedTargetReference() {
+ doTest();
+ }
+
+ public void testUnknownSourceReference() {
+ doTest();
+ }
+
+ public void testUnknownNestedSourceReference() {
+ doTest();
+ }
+
+ public void testUnknownValueMappingSourceReference() {
+ doTest();
+ }
+
+ public void testUnknownValueMappingTargetReference() {
+ doTest();
+ }
+
+ public void testUnknownIgnoreUnmappedSourceReference() {
+ doTest();
+ }
+
+ public void testUnknownQualifiedByNameReferenceReference() {
+ doTest();
+ }
+}
diff --git a/testData/inspection/UnknownIgnoreUnmappedSourceReference.java b/testData/inspection/UnknownIgnoreUnmappedSourceReference.java
new file mode 100644
index 0000000..9e0fb46
--- /dev/null
+++ b/testData/inspection/UnknownIgnoreUnmappedSourceReference.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.BeanMapping;
+
+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;
+ }
+}
+
+@Mapper
+interface SingleMappingMapper {
+
+ @Mapping(target = "testName", ignore = true)
+ @BeanMapping(ignoreUnmappedSourceProperties = {"testName"})
+ Target map(Source source);
+}
+
+@Mapper
+interface SingleMappingsMapper {
+
+ @Mappings({
+ @Mapping(target = "testName", ignore = true)
+ })
+ @BeanMapping(ignoreUnmappedSourceProperties = {"testName"})
+ Target map(Source source);
+}
\ No newline at end of file
diff --git a/testData/inspection/UnknownNestedSourceReference.java b/testData/inspection/UnknownNestedSourceReference.java
new file mode 100644
index 0000000..a3bd361
--- /dev/null
+++ b/testData/inspection/UnknownNestedSourceReference.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+
+class Source {
+ private Inner inner;
+
+ public Inner getInner() {
+ return inner;
+ }
+
+ public void setInner(Inner inner) {
+ this.inner = inner;
+ }
+}
+
+class Inner {
+
+ 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;
+ }
+}
+
+@Mapper
+interface SingleMappingMapper {
+
+ @Mapping(target = "testName", source="inner.testName")
+ Target map(Source source);
+}
+
+@Mapper
+interface SingleMappingsMapper {
+
+ @Mappings({
+ @Mapping(target = "testName", source="inner.testName")
+ })
+ Target map(Source source);
+}
\ No newline at end of file
diff --git a/testData/inspection/UnknownNestedTargetReference.java b/testData/inspection/UnknownNestedTargetReference.java
new file mode 100644
index 0000000..3df4a38
--- /dev/null
+++ b/testData/inspection/UnknownNestedTargetReference.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+
+class Source {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+class Target {
+ private Inner inner;
+
+ public Inner getInner() {
+ return inner;
+ }
+
+ public void setInner(Inner inner) {
+ this.inner = inner;
+ }
+}
+
+class Inner {
+
+ private String testName;
+
+ public String getTestName() {
+ return testName;
+ }
+
+ public void setTestName(String testName) {
+ this.testName = testName;
+ }
+}
+
+@Mapper
+interface SingleMappingMapper {
+
+ @Mapping(target = "inner.name", source="name")
+ Target map(Source source);
+}
+
+@Mapper
+interface SingleMappingsMapper {
+
+ @Mappings({
+ @Mapping(target = "inner.name", source="name")
+ })
+ Target map(Source source);
+}
\ No newline at end of file
diff --git a/testData/inspection/UnknownQualifiedByNameReferenceReference.java b/testData/inspection/UnknownQualifiedByNameReferenceReference.java
new file mode 100644
index 0000000..d413d4e
--- /dev/null
+++ b/testData/inspection/UnknownQualifiedByNameReferenceReference.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+
+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;
+ }
+}
+
+@Mapper
+interface SingleMappingMapper {
+
+ @Mapping(target = "testName", source = "name", qualifiedByName ="StringMapper")
+ Target map(Source source);
+}
+
+@Mapper
+interface SingleMappingsMapper {
+
+ @Mappings({
+ @Mapping(target = "testName", source = "name", qualifiedByName ="StringMapper")
+ })
+ Target map(Source source);
+}
\ No newline at end of file
diff --git a/testData/inspection/UnknownSourceReference.java b/testData/inspection/UnknownSourceReference.java
new file mode 100644
index 0000000..408fdf7
--- /dev/null
+++ b/testData/inspection/UnknownSourceReference.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+
+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;
+ }
+}
+
+@Mapper
+interface SingleMappingMapper {
+
+ @Mapping(target = "testName", source="testName")
+ Target map(Source source);
+}
+
+@Mapper
+interface SingleMappingsMapper {
+
+ @Mappings({
+ @Mapping(target = "testName", source="testName")
+ })
+ Target map(Source source);
+}
\ No newline at end of file
diff --git a/testData/inspection/UnknownTargetReference.java b/testData/inspection/UnknownTargetReference.java
new file mode 100644
index 0000000..641934b
--- /dev/null
+++ b/testData/inspection/UnknownTargetReference.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+
+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;
+ }
+}
+
+@Mapper
+interface SingleMappingMapper {
+
+ @Mapping(target = "name", source="name")
+ Target map(Source source);
+}
+
+@Mapper
+interface SingleMappingsMapper {
+
+ @Mappings({
+ @Mapping(target = "name", source="name")
+ })
+ Target map(Source source);
+}
\ No newline at end of file
diff --git a/testData/inspection/UnknownValueMappingSourceReference.java b/testData/inspection/UnknownValueMappingSourceReference.java
new file mode 100644
index 0000000..edd2368
--- /dev/null
+++ b/testData/inspection/UnknownValueMappingSourceReference.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Mapper;
+import org.mapstruct.ValueMapping;
+import org.mapstruct.ValueMappings;
+
+enum Target {
+ FIST,
+ SECOND,
+ THIRD
+}
+
+enum Source {
+ FIST,
+ SECOND,
+ THIRD
+}
+
+@Mapper
+interface SingleValueMappingMapper {
+
+ @ValueMapping(target = "FIST", source = "OTHER")
+ Target map(Source source);
+}
+
+@Mapper
+interface SingleValueMappingsMapper {
+
+ @ValueMappings({
+ @ValueMapping(target = "FIST", source = "OTHER")
+})
+Target map(Source source);
+}
+
diff --git a/testData/inspection/UnknownValueMappingTargetReference.java b/testData/inspection/UnknownValueMappingTargetReference.java
new file mode 100644
index 0000000..de25e12
--- /dev/null
+++ b/testData/inspection/UnknownValueMappingTargetReference.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Mapper;
+import org.mapstruct.ValueMapping;
+import org.mapstruct.ValueMappings;
+
+enum Target {
+ FIST,
+ SECOND,
+ THIRD
+}
+
+enum Source {
+ FIST,
+ SECOND,
+ THIRD
+}
+
+@Mapper
+interface SingleValueMappingMapper {
+
+ @ValueMapping(target = "OTHER", source = "FIST")
+ Target map(Source source);
+}
+
+@Mapper
+interface SingleValueMappingsMapper {
+
+ @ValueMappings({
+ @ValueMapping(target = "OTHER", source = "FIST")
+})
+Target map(Source source);
+}
+