Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add unknown reference inspection #213

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions description.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
<li>More than one default source in <code>@Mapping</code> annotation defined with quick fixes: Remove <code>defaultValue</code>. Remove <code>defaultExpression</code>.</li>
<li><code>target</code> mapped more than once by <code>@Mapping</code> annotations with quick fixes: Remove annotation and change target property.</li>
<li><code>*</code> used as a source in <code>@Mapping</code> annotations with quick fixes: Replace <code>*</code> with method parameter name.</li>
<li>Unknown reference inspection for <code>source</code> and <code>target</code> in <code>@Mapping</code> and <code>@ValueMapping</code> annotation. </li>
<li>Unknown reference inspection for <code>qualifiedByName</code> in <code>@Mapping</code> annotation. </li>
</ul>
</li>
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*
* @author Filip Hrisafov
*/
abstract class BaseReference extends PsiReferenceBase<PsiElement> {
public abstract class BaseReference extends PsiReferenceBase<PsiElement> {

/**
* @param element the element for which a reference should be found
Expand Down
Original file line number Diff line number Diff line change
@@ -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 );
}
}
}
8 changes: 8 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@
key="inspection.source.property.this.used"
shortName="ThisUsedAsSourcePropertyInspection"
implementationClass="org.mapstruct.intellij.inspection.ThisUsedAsSourcePropertyInspection"/>
<localInspection
language="JAVA"
enabledByDefault="true"
level="ERROR"
bundle="org.mapstruct.intellij.messages.MapStructBundle"
key="inspection.mapstruct.references"
shortName="MapstructReferenceInspection"
implementationClass="org.mapstruct.intellij.inspection.MapstructReferenceInspection"/>
</extensions>

<actions>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<body>
This inspection reports unresolved mapstruct references.
<pre><code lang="java">
@Mapper
public interface EmployeeMapper {
@Mapping(target = "dto", source = "no_exists") // highlighted if source doesn't exist
Employee toEmployee(EmployeeDto employeeDto, @Context CycleAvoidingMappingContext context);
}
</code></pre>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
54 changes: 54 additions & 0 deletions testData/inspection/UnknownIgnoreUnmappedSourceReference.java
Original file line number Diff line number Diff line change
@@ -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 = {"<error descr="Cannot resolve symbol 'testName'">testName</error>"})
Target map(Source source);
}

@Mapper
interface SingleMappingsMapper {

@Mappings({
@Mapping(target = "testName", ignore = true)
})
@BeanMapping(ignoreUnmappedSourceProperties = {"<error descr="Cannot resolve symbol 'testName'">testName</error>"})
Target map(Source source);
}
63 changes: 63 additions & 0 deletions testData/inspection/UnknownNestedSourceReference.java
Original file line number Diff line number Diff line change
@@ -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.<error descr="Cannot resolve symbol 'testName'">testName</error>")
Target map(Source source);
}

@Mapper
interface SingleMappingsMapper {

@Mappings({
@Mapping(target = "testName", source="inner.<error descr="Cannot resolve symbol 'testName'">testName</error>")
})
Target map(Source source);
}
63 changes: 63 additions & 0 deletions testData/inspection/UnknownNestedTargetReference.java
Original file line number Diff line number Diff line change
@@ -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.<error descr="Cannot resolve symbol 'name'">name</error>", source="name")
Target map(Source source);
}

@Mapper
interface SingleMappingsMapper {

@Mappings({
@Mapping(target = "inner.<error descr="Cannot resolve symbol 'name'">name</error>", source="name")
})
Target map(Source source);
}
Loading
Loading