Skip to content

Commit cf8d6b6

Browse files
authored
#178 Add unknown reference inspection
1 parent fe429d9 commit cf8d6b6

16 files changed

+549
-1
lines changed

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ To learn more about MapStruct have a look at the [mapstruct](https://github.com/
3939
* More than one default source in `@Mapping` annotation defined with quick fixes: Remove `defaultValue`. Remove `defaultExpression`.
4040
* `target` mapped more than once by `@Mapping` annotations with quick fixes: Remove annotation and change target property.
4141
* `*` used as a source in `@Mapping` annotation with quick fixes: Replace `*` with method parameter name.
42+
* Unknown reference inspection for `source` and `target` in `@Mapping` and `@ValueMapping` annotation.
43+
* Unknown reference inspection for `qualifiedByName` in `@Mapping` annotation
4244

4345
## Requirements
4446

Diff for: description.html

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
<li>More than one default source in <code>@Mapping</code> annotation defined with quick fixes: Remove <code>defaultValue</code>. Remove <code>defaultExpression</code>.</li>
4444
<li><code>target</code> mapped more than once by <code>@Mapping</code> annotations with quick fixes: Remove annotation and change target property.</li>
4545
<li><code>*</code> used as a source in <code>@Mapping</code> annotations with quick fixes: Replace <code>*</code> with method parameter name.</li>
46+
<li>Unknown reference inspection for <code>source</code> and <code>target</code> in <code>@Mapping</code> and <code>@ValueMapping</code> annotation. </li>
47+
<li>Unknown reference inspection for <code>qualifiedByName</code> in <code>@Mapping</code> annotation. </li>
4648
</ul>
4749
</li>
4850
</ul>

Diff for: src/main/java/org/mapstruct/intellij/codeinsight/references/BaseReference.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*
2222
* @author Filip Hrisafov
2323
*/
24-
abstract class BaseReference extends PsiReferenceBase<PsiElement> {
24+
public abstract class BaseReference extends PsiReferenceBase<PsiElement> {
2525

2626
/**
2727
* @param element the element for which a reference should be found
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.intellij.inspection;
7+
8+
import com.intellij.codeInspection.ProblemHighlightType;
9+
import com.intellij.codeInspection.ProblemsHolder;
10+
import com.intellij.openapi.util.TextRange;
11+
import com.intellij.psi.ContributedReferenceHost;
12+
import com.intellij.psi.PsiElement;
13+
import com.intellij.psi.PsiElementVisitor;
14+
import com.intellij.psi.PsiLanguageInjectionHost;
15+
import com.intellij.psi.PsiReference;
16+
import org.jetbrains.annotations.NotNull;
17+
import org.mapstruct.intellij.codeinsight.references.BaseReference;
18+
19+
/**
20+
* Inspection that checks if mapstruct references can be resolved.
21+
* @see BaseReference
22+
* @author hduelme
23+
*/
24+
public class MapstructReferenceInspection extends InspectionBase {
25+
26+
@Override
27+
@NotNull PsiElementVisitor buildVisitorInternal(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
28+
return new MapstructReferenceVisitor(holder);
29+
}
30+
31+
private static class MapstructReferenceVisitor extends PsiElementVisitor {
32+
33+
private final ProblemsHolder holder;
34+
35+
private MapstructReferenceVisitor(ProblemsHolder holder) {
36+
this.holder = holder;
37+
}
38+
39+
/**
40+
* Based on org.intellij.plugins.intelliLang.references.InjectedReferencesInspection
41+
*/
42+
@Override
43+
public void visitElement(@NotNull PsiElement element) {
44+
if (element instanceof ContributedReferenceHost r && element instanceof PsiLanguageInjectionHost) {
45+
for (PsiReference psiReference : r.getReferences()) {
46+
if (psiReference instanceof BaseReference && psiReference.resolve() == null) {
47+
TextRange range = psiReference.getRangeInElement();
48+
if (range.isEmpty() && range.getStartOffset() == 1 && "\"\"".equals( element.getText() ) ) {
49+
String message = ProblemsHolder.unresolvedReferenceMessage( psiReference );
50+
holder.registerProblem( element, message, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL,
51+
TextRange.create( 0, 2 ) );
52+
}
53+
else {
54+
holder.registerProblem( psiReference );
55+
}
56+
}
57+
}
58+
}
59+
super.visitElement( element );
60+
}
61+
}
62+
}

Diff for: src/main/resources/META-INF/plugin.xml

+8
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@
147147
key="inspection.source.property.this.used"
148148
shortName="ThisUsedAsSourcePropertyInspection"
149149
implementationClass="org.mapstruct.intellij.inspection.ThisUsedAsSourcePropertyInspection"/>
150+
<localInspection
151+
language="JAVA"
152+
enabledByDefault="true"
153+
level="ERROR"
154+
bundle="org.mapstruct.intellij.messages.MapStructBundle"
155+
key="inspection.mapstruct.references"
156+
shortName="MapstructReferenceInspection"
157+
implementationClass="org.mapstruct.intellij.inspection.MapstructReferenceInspection"/>
150158
</extensions>
151159

152160
<actions>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<html>
2+
<body>
3+
This inspection reports unresolved mapstruct references.
4+
<pre><code lang="java">
5+
@Mapper
6+
public interface EmployeeMapper {
7+
@Mapping(target = "dto", source = "no_exists") // highlighted if source doesn't exist
8+
Employee toEmployee(EmployeeDto employeeDto, @Context CycleAvoidingMappingContext context);
9+
}
10+
</code></pre>
11+
</body>
12+
</html>

Diff for: src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ inspection.wrong.map.mapping.map.key.change.to.string=Change key type to String
2828
inspection.target.property.mapped.more.than.once=Target property ''{0}'' must not be mapped more than once.
2929
inspection.target.property.mapped.more.than.once.title=Target properties must not be mapped more than once.
3030
inspection.source.property.this.used=''.'' should not be used as a source.
31+
inspection.mapstruct.references=Injected mapstruct references
3132
intention.add.ignore.all.unmapped.target.properties=Add ignore all unmapped target properties
3233
intention.add.ignore.unmapped.target.property=Add ignore unmapped target property
3334
intention.add.unmapped.target.property=Add unmapped target property
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.intellij.inspection;
7+
8+
import com.intellij.codeInspection.LocalInspectionTool;
9+
import org.jetbrains.annotations.NotNull;
10+
11+
/**
12+
* @author hduelme
13+
*/
14+
public class MapstructReferenceInspectionTest extends BaseInspectionTest {
15+
16+
@Override
17+
protected @NotNull Class<? extends LocalInspectionTool> getInspection() {
18+
return MapstructReferenceInspection.class;
19+
}
20+
21+
public void testUnknownTargetReference() {
22+
doTest();
23+
}
24+
25+
public void testUnknownNestedTargetReference() {
26+
doTest();
27+
}
28+
29+
public void testUnknownSourceReference() {
30+
doTest();
31+
}
32+
33+
public void testUnknownNestedSourceReference() {
34+
doTest();
35+
}
36+
37+
public void testUnknownValueMappingSourceReference() {
38+
doTest();
39+
}
40+
41+
public void testUnknownValueMappingTargetReference() {
42+
doTest();
43+
}
44+
45+
public void testUnknownIgnoreUnmappedSourceReference() {
46+
doTest();
47+
}
48+
49+
public void testUnknownQualifiedByNameReferenceReference() {
50+
doTest();
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
7+
import org.mapstruct.Mapper;
8+
import org.mapstruct.Mapping;
9+
import org.mapstruct.Mappings;
10+
import org.mapstruct.BeanMapping;
11+
12+
class Source {
13+
14+
private String name;
15+
16+
public String getName() {
17+
return name;
18+
}
19+
20+
public void setName(String name) {
21+
this.name = name;
22+
}
23+
}
24+
25+
class Target {
26+
27+
private String testName;
28+
29+
public String getTestName() {
30+
return testName;
31+
}
32+
33+
public void setTestName(String testName) {
34+
this.testName = testName;
35+
}
36+
}
37+
38+
@Mapper
39+
interface SingleMappingMapper {
40+
41+
@Mapping(target = "testName", ignore = true)
42+
@BeanMapping(ignoreUnmappedSourceProperties = {"<error descr="Cannot resolve symbol 'testName'">testName</error>"})
43+
Target map(Source source);
44+
}
45+
46+
@Mapper
47+
interface SingleMappingsMapper {
48+
49+
@Mappings({
50+
@Mapping(target = "testName", ignore = true)
51+
})
52+
@BeanMapping(ignoreUnmappedSourceProperties = {"<error descr="Cannot resolve symbol 'testName'">testName</error>"})
53+
Target map(Source source);
54+
}
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
7+
import org.mapstruct.Mapper;
8+
import org.mapstruct.Mapping;
9+
import org.mapstruct.Mappings;
10+
11+
class Source {
12+
private Inner inner;
13+
14+
public Inner getInner() {
15+
return inner;
16+
}
17+
18+
public void setInner(Inner inner) {
19+
this.inner = inner;
20+
}
21+
}
22+
23+
class Inner {
24+
25+
private String name;
26+
27+
public String getName() {
28+
return name;
29+
}
30+
31+
public void setName(String name) {
32+
this.name = name;
33+
}
34+
}
35+
36+
class Target {
37+
38+
private String testName;
39+
40+
public String getTestName() {
41+
return testName;
42+
}
43+
44+
public void setTestName(String testName) {
45+
this.testName = testName;
46+
}
47+
}
48+
49+
@Mapper
50+
interface SingleMappingMapper {
51+
52+
@Mapping(target = "testName", source="inner.<error descr="Cannot resolve symbol 'testName'">testName</error>")
53+
Target map(Source source);
54+
}
55+
56+
@Mapper
57+
interface SingleMappingsMapper {
58+
59+
@Mappings({
60+
@Mapping(target = "testName", source="inner.<error descr="Cannot resolve symbol 'testName'">testName</error>")
61+
})
62+
Target map(Source source);
63+
}
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
7+
import org.mapstruct.Mapper;
8+
import org.mapstruct.Mapping;
9+
import org.mapstruct.Mappings;
10+
11+
class Source {
12+
13+
private String name;
14+
15+
public String getName() {
16+
return name;
17+
}
18+
19+
public void setName(String name) {
20+
this.name = name;
21+
}
22+
}
23+
24+
class Target {
25+
private Inner inner;
26+
27+
public Inner getInner() {
28+
return inner;
29+
}
30+
31+
public void setInner(Inner inner) {
32+
this.inner = inner;
33+
}
34+
}
35+
36+
class Inner {
37+
38+
private String testName;
39+
40+
public String getTestName() {
41+
return testName;
42+
}
43+
44+
public void setTestName(String testName) {
45+
this.testName = testName;
46+
}
47+
}
48+
49+
@Mapper
50+
interface SingleMappingMapper {
51+
52+
@Mapping(target = "inner.<error descr="Cannot resolve symbol 'name'">name</error>", source="name")
53+
Target map(Source source);
54+
}
55+
56+
@Mapper
57+
interface SingleMappingsMapper {
58+
59+
@Mappings({
60+
@Mapping(target = "inner.<error descr="Cannot resolve symbol 'name'">name</error>", source="name")
61+
})
62+
Target map(Source source);
63+
}

0 commit comments

Comments
 (0)