#2125 Report an error when source parameter could not be determined from target mapping

This commit is contained in:
Filip Hrisafov 2020-09-20 12:48:47 +02:00
parent e17e744b20
commit 060f17e3e2
9 changed files with 214 additions and 5 deletions

View File

@ -65,6 +65,8 @@ import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_NOT_ASSIGNABLE;
import static org.mapstruct.ap.internal.util.Message.GENERAL_ABSTRACT_RETURN_TYPE;
import static org.mapstruct.ap.internal.util.Message.GENERAL_AMBIGUOUS_CONSTRUCTORS;
import static org.mapstruct.ap.internal.util.Message.GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS;
import static org.mapstruct.ap.internal.util.Message.PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET;
import static org.mapstruct.ap.internal.util.Message.PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PROPERTY_FROM_TARGET;
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one bean type to another, optionally
@ -1113,6 +1115,31 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
errorOccured = true;
}
}
else {
errorOccured = true;
if ( method.getSourceParameters().size() == 1 ) {
ctx.getMessager()
.printMessage(
method.getExecutable(),
mapping.getMirror(),
mapping.getTargetAnnotationValue(),
PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PROPERTY_FROM_TARGET,
method.getSourceParameters().get( 0 ).getName(),
targetPropertyName
);
}
else {
ctx.getMessager()
.printMessage(
method.getExecutable(),
mapping.getMirror(),
mapping.getTargetAnnotationValue(),
PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET,
targetPropertyName
);
}
}
}
// remaining are the mappings without a 'source' so, 'only' a date format or qualifiers
if ( propertyMapping != null ) {

View File

@ -73,6 +73,8 @@ public enum Message {
PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE( "No read accessor found for property \"%s\" in target type." ),
PROPERTYMAPPING_NO_WRITE_ACCESSOR_FOR_TARGET_TYPE( "No write accessor found for property \"%s\" in target type." ),
PROPERTYMAPPING_WHITESPACE_TRIMMED( "The property named \"%s\" has whitespaces, using trimmed property \"%s\" instead.", Diagnostic.Kind.WARNING ),
PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PROPERTY_FROM_TARGET("The type of parameter \"%s\" has no property named \"%s\". Please define the source property explicitly."),
PROPERTYMAPPING_CANNOT_DETERMINE_SOURCE_PARAMETER_FROM_TARGET("No property named \"%s\" exists in source parameter(s). Please define the source explicitly."),
CONVERSION_LOSSY_WARNING( "%s has a possibly lossy conversion from %s to %s.", Diagnostic.Kind.WARNING ),
CONVERSION_LOSSY_ERROR( "Can't map %s. It has a possibly lossy conversion from %s to %s." ),

View File

@ -8,7 +8,6 @@ package org.mapstruct.ap.test.bugs._2077;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
@ -16,8 +15,6 @@ import org.mapstruct.factory.Mappers;
@Mapper( unmappedTargetPolicy = ReportingPolicy.ERROR )
public interface Issue2077ErroneousMapper {
Issue2077ErroneousMapper INSTANCE = Mappers.getMapper( Issue2077ErroneousMapper.class );
@Mapping(target = "s1", defaultValue = "xyz" )
Target map(String source);

View File

@ -30,8 +30,9 @@ public class Issue2077Test {
diagnostics = {
@Diagnostic(type = Issue2077ErroneousMapper.class,
kind = ERROR,
line = 22,
message = "Unmapped target property: \"s1\".")
line = 18,
message = "The type of parameter \"source\" has no property named \"s1\". Please define the source " +
"property explicitly.")
}
)
public void shouldNotCompile() {

View File

@ -0,0 +1,27 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2125;
/**
* @author Filip Hrisafov
*/
public class Comment {
private final Integer issueId;
private final String comment;
public Comment(Integer issueId, String comment) {
this.issueId = issueId;
this.comment = comment;
}
public Integer getIssueId() {
return issueId;
}
public String getComment() {
return comment;
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2125;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
@Mapper
public interface Issue2125ErroneousMapper {
@Mapping(target = "issueId", qualifiedByName = "mapIssueNumber")
@Mapping( target = "comment", ignore = true)
Comment clone(Repository repository);
@Mapping(target = "issueId", qualifiedByName = "mapIssueNumber")
@Mapping( target = "comment", ignore = true)
Comment clone(Comment comment, Repository repository);
@Named("mapIssueNumber")
default Integer mapIssueNumber(Integer issueNumber) {
return issueNumber != null ? issueNumber + 1 : null;
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2125;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
@Mapper
public interface Issue2125Mapper {
Issue2125Mapper INSTANCE = Mappers.getMapper( Issue2125Mapper.class );
Comment clone(Comment comment, Integer issueId);
@Mapping(target = "issueId", qualifiedByName = "mapIssueNumber")
Comment cloneWithQualifier(Comment comment, Integer issueId);
@Named("mapIssueNumber")
default Integer mapIssueNumber(Integer issueNumber) {
return issueNumber != null ? issueNumber + 1 : null;
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2125;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import static org.assertj.core.api.Assertions.assertThat;
@IssueKey("2125")
@WithClasses({
Comment.class,
Repository.class,
})
@RunWith(AnnotationProcessorTestRunner.class)
public class Issue2125Test {
@Test
@WithClasses({
Issue2125Mapper.class
})
public void shouldSelectProperMethod() {
Comment comment = Issue2125Mapper.INSTANCE.clone(
new Comment( 2125, "Fix issue" ),
1000
);
assertThat( comment ).isNotNull();
assertThat( comment.getIssueId() ).isEqualTo( 2125 );
comment = Issue2125Mapper.INSTANCE.cloneWithQualifier(
new Comment( 2125, "Fix issue" ),
1000
);
assertThat( comment ).isNotNull();
assertThat( comment.getIssueId() ).isEqualTo( 1001 );
}
@Test
@WithClasses({
Issue2125ErroneousMapper.class
})
@ExpectedCompilationOutcome(value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = Issue2125ErroneousMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 15,
alternativeLine = 17, // For some reason javac reports the error on the method instead of the annotation
message = "The type of parameter \"repository\" has no property named \"issueId\". Please define the " +
"source property explicitly."),
@Diagnostic(type = Issue2125ErroneousMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 19,
alternativeLine = 21, // For some reason javac reports the error on the method instead of the annotation
message = "No property named \"issueId\" exists in source parameter(s). Please define the source " +
"explicitly.")
})
public void shouldReportErrorWhenMultipleSourcesMatch() {
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2125;
/**
* @author Filip Hrisafov
*/
public class Repository {
private final String owner;
private final String name;
public Repository(String owner, String name) {
this.owner = owner;
this.name = name;
}
public String getOwner() {
return owner;
}
public String getName() {
return name;
}
}