#2156 ambiguous mapping message: location and limit # candidates (#2162)

This commit is contained in:
Sjaak Derksen 2020-07-19 15:40:30 +02:00 committed by GitHub
parent cb432fa61b
commit 36349c49e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 287 additions and 56 deletions

View File

@ -27,6 +27,8 @@ public abstract class AbstractMappingMethodBuilder<B extends AbstractMappingMeth
public abstract M build(); public abstract M build();
private ForgedMethodHistory description;
/** /**
* @return {@code true} if property names should be used for the creation of the {@link ForgedMethodHistory}. * @return {@code true} if property names should be used for the creation of the {@link ForgedMethodHistory}.
*/ */
@ -44,7 +46,7 @@ public abstract class AbstractMappingMethodBuilder<B extends AbstractMappingMeth
history = ( (ForgedMethod) method ).getHistory(); history = ( (ForgedMethod) method ).getHistory();
} }
ForgedMethodHistory forgedHistory = new ForgedMethodHistory( description = new ForgedMethodHistory(
history, history,
Strings.stubPropertyName( sourceRHS.getSourceType().getName() ), Strings.stubPropertyName( sourceRHS.getSourceType().getName() ),
Strings.stubPropertyName( targetType.getName() ), Strings.stubPropertyName( targetType.getName() ),
@ -53,7 +55,7 @@ public abstract class AbstractMappingMethodBuilder<B extends AbstractMappingMeth
shouldUsePropertyNamesInHistory(), shouldUsePropertyNamesInHistory(),
sourceRHS.getSourceErrorMessagePart() ); sourceRHS.getSourceErrorMessagePart() );
ForgedMethod forgedMethod = forElementMapping( name, sourceType, targetType, method, forgedHistory, true ); ForgedMethod forgedMethod = forElementMapping( name, sourceType, targetType, method, description, true );
BuilderGem builder = method.getOptions().getBeanMapping().getBuilder(); BuilderGem builder = method.getOptions().getBeanMapping().getBuilder();
return createForgedAssignment( return createForgedAssignment(
@ -77,4 +79,9 @@ public abstract class AbstractMappingMethodBuilder<B extends AbstractMappingMeth
builder.append( type.getIdentification() ); builder.append( type.getIdentification() );
return builder.toString(); return builder.toString();
} }
public ForgedMethodHistory getDescription() {
return description;
}
} }

View File

@ -62,7 +62,7 @@ import static org.mapstruct.ap.internal.util.Collections.first;
import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_ABSTRACT; import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_ABSTRACT;
import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_NOT_ASSIGNABLE; 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_ABSTRACT_RETURN_TYPE;
import static org.mapstruct.ap.internal.util.Message.GENERAL_AMBIGIOUS_CONSTRUCTORS; 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.GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS;
/** /**
@ -569,7 +569,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
else { else {
ctx.getMessager().printMessage( ctx.getMessager().printMessage(
method.getExecutable(), method.getExecutable(),
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD, Message.GENERAL_AMBIGUOUS_FACTORY_METHOD,
returnTypeImpl, returnTypeImpl,
Strings.join( matchingFactoryMethods, ", " ) Strings.join( matchingFactoryMethods, ", " )
); );
@ -665,7 +665,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
ctx.getMessager().printMessage( ctx.getMessager().printMessage(
method.getExecutable(), method.getExecutable(),
GENERAL_AMBIGIOUS_CONSTRUCTORS, GENERAL_AMBIGUOUS_CONSTRUCTORS,
type, type,
Strings.join( constructors, ", " ) Strings.join( constructors, ", " )
); );

View File

@ -83,6 +83,7 @@ public abstract class ContainerMappingMethodBuilder<B extends ContainerMappingMe
); );
Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method, Assignment assignment = ctx.getMappingResolver().getTargetAssignment( method,
getDescription(),
targetElementType, targetElementType,
formattingParameters, formattingParameters,
criteria, criteria,

View File

@ -96,6 +96,7 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment( Assignment keyAssignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
getDescription(),
keyTargetType, keyTargetType,
keyFormattingParameters, keyFormattingParameters,
keyCriteria, keyCriteria,
@ -142,6 +143,7 @@ public class MapMappingMethod extends NormalTypeMappingMethod {
Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment( Assignment valueAssignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
getDescription(),
valueTargetType, valueTargetType,
valueFormattingParameters, valueFormattingParameters,
valueCriteria, valueCriteria,

View File

@ -78,6 +78,7 @@ public class MappingBuilderContext {
* returns a parameter assignment * returns a parameter assignment
* *
* @param mappingMethod target mapping method * @param mappingMethod target mapping method
* @param description
* @param targetType return type to match * @param targetType return type to match
* @param formattingParameters used for formatting dates and numbers * @param formattingParameters used for formatting dates and numbers
* @param criteria parameters criteria in the selection process * @param criteria parameters criteria in the selection process
@ -92,7 +93,7 @@ public class MappingBuilderContext {
* <li>null, no assignment found</li> * <li>null, no assignment found</li>
* </ol> * </ol>
*/ */
Assignment getTargetAssignment(Method mappingMethod, Type targetType, Assignment getTargetAssignment(Method mappingMethod, ForgedMethodHistory description, Type targetType,
FormattingParameters formattingParameters, FormattingParameters formattingParameters,
SelectionCriteria criteria, SourceRHS sourceRHS, SelectionCriteria criteria, SourceRHS sourceRHS,
AnnotationMirror positionHint, AnnotationMirror positionHint,

View File

@ -81,7 +81,7 @@ public class ObjectFactoryMethodResolver {
if ( matchingFactoryMethods.size() > 1 ) { if ( matchingFactoryMethods.size() > 1 ) {
ctx.getMessager().printMessage( ctx.getMessager().printMessage(
method.getExecutable(), method.getExecutable(),
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD, Message.GENERAL_AMBIGUOUS_FACTORY_METHOD,
alternativeTarget, alternativeTarget,
Strings.join( matchingFactoryMethods, ", " ) ); Strings.join( matchingFactoryMethods, ", " ) );

View File

@ -242,6 +242,7 @@ public class PropertyMapping extends ModelElement {
if ( forgeMethodWithMappingReferences == null ) { if ( forgeMethodWithMappingReferences == null ) {
assignment = ctx.getMappingResolver().getTargetAssignment( assignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
getForgedMethodHistory( rightHandSide ),
targetType, targetType,
formattingParameters, formattingParameters,
criteria, criteria,
@ -791,6 +792,7 @@ public class PropertyMapping extends ModelElement {
if ( !targetType.isEnumType() ) { if ( !targetType.isEnumType() ) {
assignment = ctx.getMappingResolver().getTargetAssignment( assignment = ctx.getMappingResolver().getTargetAssignment(
method, method,
null, // TODO description for constant
targetType, targetType,
formattingParameters, formattingParameters,
criteria, criteria,

View File

@ -39,6 +39,7 @@ import org.mapstruct.ap.internal.conversion.ConversionProvider;
import org.mapstruct.ap.internal.conversion.Conversions; import org.mapstruct.ap.internal.conversion.Conversions;
import org.mapstruct.ap.internal.gem.ReportingPolicyGem; import org.mapstruct.ap.internal.gem.ReportingPolicyGem;
import org.mapstruct.ap.internal.model.Field; import org.mapstruct.ap.internal.model.Field;
import org.mapstruct.ap.internal.model.ForgedMethodHistory;
import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.HelperMethod;
import org.mapstruct.ap.internal.model.MapperReference; import org.mapstruct.ap.internal.model.MapperReference;
import org.mapstruct.ap.internal.model.MappingBuilderContext.MappingResolver; import org.mapstruct.ap.internal.model.MappingBuilderContext.MappingResolver;
@ -74,6 +75,8 @@ import org.mapstruct.ap.internal.util.Strings;
*/ */
public class MappingResolverImpl implements MappingResolver { public class MappingResolverImpl implements MappingResolver {
private static final int MAX_REPORTING_AMBIGUOUS = 5;
private final FormattingMessager messager; private final FormattingMessager messager;
private final Types typeUtils; private final Types typeUtils;
private final TypeFactory typeFactory; private final TypeFactory typeFactory;
@ -109,7 +112,7 @@ public class MappingResolverImpl implements MappingResolver {
} }
@Override @Override
public Assignment getTargetAssignment(Method mappingMethod, Type targetType, public Assignment getTargetAssignment(Method mappingMethod, ForgedMethodHistory description, Type targetType,
FormattingParameters formattingParameters, FormattingParameters formattingParameters,
SelectionCriteria criteria, SourceRHS sourceRHS, SelectionCriteria criteria, SourceRHS sourceRHS,
AnnotationMirror positionHint, AnnotationMirror positionHint,
@ -118,6 +121,7 @@ public class MappingResolverImpl implements MappingResolver {
ResolvingAttempt attempt = new ResolvingAttempt( ResolvingAttempt attempt = new ResolvingAttempt(
sourceModel, sourceModel,
mappingMethod, mappingMethod,
description,
formattingParameters, formattingParameters,
sourceRHS, sourceRHS,
criteria, criteria,
@ -149,6 +153,7 @@ public class MappingResolverImpl implements MappingResolver {
private class ResolvingAttempt { private class ResolvingAttempt {
private final Method mappingMethod; private final Method mappingMethod;
private final ForgedMethodHistory description;
private final List<Method> methods; private final List<Method> methods;
private final SelectionCriteria selectionCriteria; private final SelectionCriteria selectionCriteria;
private final SourceRHS sourceRHS; private final SourceRHS sourceRHS;
@ -163,7 +168,7 @@ public class MappingResolverImpl implements MappingResolver {
// so this set must be cleared. // so this set must be cleared.
private final Set<SupportingMappingMethod> supportingMethodCandidates; private final Set<SupportingMappingMethod> supportingMethodCandidates;
private ResolvingAttempt(List<Method> sourceModel, Method mappingMethod, private ResolvingAttempt(List<Method> sourceModel, Method mappingMethod, ForgedMethodHistory description,
FormattingParameters formattingParameters, SourceRHS sourceRHS, FormattingParameters formattingParameters, SourceRHS sourceRHS,
SelectionCriteria criteria, SelectionCriteria criteria,
AnnotationMirror positionHint, AnnotationMirror positionHint,
@ -172,6 +177,7 @@ public class MappingResolverImpl implements MappingResolver {
FormattingMessager messager) { FormattingMessager messager) {
this.mappingMethod = mappingMethod; this.mappingMethod = mappingMethod;
this.description = description;
this.methods = filterPossibleCandidateMethods( sourceModel ); this.methods = filterPossibleCandidateMethods( sourceModel );
this.formattingParameters = this.formattingParameters =
formattingParameters == null ? FormattingParameters.EMPTY : formattingParameters; formattingParameters == null ? FormattingParameters.EMPTY : formattingParameters;
@ -202,7 +208,7 @@ public class MappingResolverImpl implements MappingResolver {
// first simple mapping method // first simple mapping method
if ( allowMappingMethod() ) { if ( allowMappingMethod() ) {
List<SelectedMethod<Method>> matches = getBestMatch( methods, sourceType, targetType ); List<SelectedMethod<Method>> matches = getBestMatch( methods, sourceType, targetType );
reportErrorWhenAmbigious( matches, targetType ); reportErrorWhenAmbiguous( matches, targetType );
if ( !matches.isEmpty() ) { if ( !matches.isEmpty() ) {
assignment = toMethodRef( first( matches ) ); assignment = toMethodRef( first( matches ) );
assignment.setAssignment( sourceRHS ); assignment.setAssignment( sourceRHS );
@ -246,7 +252,7 @@ public class MappingResolverImpl implements MappingResolver {
// check for a built-in method // check for a built-in method
if ( !hasQualfiers() ) { if ( !hasQualfiers() ) {
List<SelectedMethod<BuiltInMethod>> matches = getBestMatch( builtIns, sourceType, targetType ); List<SelectedMethod<BuiltInMethod>> matches = getBestMatch( builtIns, sourceType, targetType );
reportErrorWhenAmbigious( matches, targetType ); reportErrorWhenAmbiguous( matches, targetType );
if ( !matches.isEmpty() ) { if ( !matches.isEmpty() ) {
assignment = toBuildInRef( first( matches ) ); assignment = toBuildInRef( first( matches ) );
assignment.setAssignment( sourceRHS ); assignment.setAssignment( sourceRHS );
@ -443,29 +449,37 @@ public class MappingResolverImpl implements MappingResolver {
); );
} }
private <T extends Method> void reportErrorWhenAmbigious(List<SelectedMethod<T>> candidates, Type target) { private <T extends Method> void reportErrorWhenAmbiguous(List<SelectedMethod<T>> candidates, Type target) {
// raise an error if more than one mapping method is suitable to map the given source type // raise an error if more than one mapping method is suitable to map the given source type
// into the target type // into the target type
if ( candidates.size() > 1 ) { if ( candidates.size() > 1 ) {
String descriptionStr = "";
if ( description != null ) {
descriptionStr = description.createSourcePropertyErrorMessage();
}
else {
descriptionStr = sourceRHS.getSourceErrorMessagePart();
}
if ( sourceRHS.getSourceErrorMessagePart() != null ) { if ( sourceRHS.getSourceErrorMessagePart() != null ) {
messager.printMessage( messager.printMessage(
mappingMethod.getExecutable(), mappingMethod.getExecutable(),
positionHint, positionHint,
Message.GENERAL_AMBIGIOUS_MAPPING_METHOD, Message.GENERAL_AMBIGUOUS_MAPPING_METHOD,
sourceRHS.getSourceErrorMessagePart(), descriptionStr,
target, target,
Strings.join( candidates, ", " ) join( candidates )
); );
} }
else { else {
messager.printMessage( messager.printMessage(
mappingMethod.getExecutable(), mappingMethod.getExecutable(),
positionHint, positionHint,
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD, Message.GENERAL_AMBIGUOUS_FACTORY_METHOD,
target, target,
Strings.join( candidates, ", " ) join( candidates )
); );
} }
} }
@ -578,6 +592,18 @@ public class MappingResolverImpl implements MappingResolver {
return false; return false;
} }
private <T extends Method> String join( List<SelectedMethod<T>> candidates ) {
String candidateStr = candidates.stream()
.limit( MAX_REPORTING_AMBIGUOUS )
.map( m -> m.getMethod().shortName() )
.collect( Collectors.joining( ", " ) );
if ( candidates.size() > MAX_REPORTING_AMBIGUOUS ) {
candidateStr += String.format( "... and %s more", candidates.size() - MAX_REPORTING_AMBIGUOUS );
}
return candidateStr;
}
} }
private static class ConversionAssignment { private static class ConversionAssignment {
@ -762,28 +788,26 @@ public class MappingResolverImpl implements MappingResolver {
result = methodRefY; result = methodRefY;
} }
else { else {
reportAmbigiousError( xCandidates, targetType ); reportAmbiguousError( xCandidates, targetType );
} }
return this; return this;
} }
void reportAmbigiousError(Map<SelectedMethod<T1>, List<SelectedMethod<T2>>> xCandidates, Type target) { void reportAmbiguousError(Map<SelectedMethod<T1>, List<SelectedMethod<T2>>> xCandidates, Type target) {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
xCandidates.entrySet() xCandidates.entrySet()
.stream() .stream()
.limit( MAX_REPORTING_AMBIGUOUS )
.forEach( e -> result.append( "method(s)Y: " ) .forEach( e -> result.append( "method(s)Y: " )
.append( e.getValue() .append( attempt.join( e.getValue() ) )
.stream()
.map( v -> v.getMethod().shortName() )
.collect( Collectors.joining( ", " ) ) )
.append( ", methodX: " ) .append( ", methodX: " )
.append( e.getKey().getMethod().shortName() ) .append( e.getKey().getMethod().shortName() )
.append( "; " ) ); .append( "; " ) );
attempt.messager.printMessage( attempt.messager.printMessage(
attempt.mappingMethod.getExecutable(), attempt.mappingMethod.getExecutable(),
attempt.positionHint, attempt.positionHint,
Message.GENERAL_AMBIGIOUS_MAPPING_METHODY_METHODX, Message.GENERAL_AMBIGUOUS_MAPPING_METHODY_METHODX,
attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(),
target.getName(), target.getName(),
result.toString() ); result.toString() );
@ -878,28 +902,26 @@ public class MappingResolverImpl implements MappingResolver {
result = methodRefY; result = methodRefY;
} }
else { else {
reportAmbigiousError( xRefCandidates, targetType ); reportAmbiguousError( xRefCandidates, targetType );
} }
return this; return this;
} }
void reportAmbigiousError(Map<ConversionAssignment, List<SelectedMethod<T>>> xRefCandidates, Type target) { void reportAmbiguousError(Map<ConversionAssignment, List<SelectedMethod<T>>> xRefCandidates, Type target) {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
xRefCandidates.entrySet() xRefCandidates.entrySet()
.stream() .stream()
.limit( MAX_REPORTING_AMBIGUOUS )
.forEach( e -> result.append( "method(s)Y: " ) .forEach( e -> result.append( "method(s)Y: " )
.append( e.getValue() .append( attempt.join( e.getValue() ) )
.stream()
.map( v -> v.getMethod().shortName() )
.collect( Collectors.joining( ", " ) ) )
.append( ", conversionX: " ) .append( ", conversionX: " )
.append( e.getKey().shortName() ) .append( e.getKey().shortName() )
.append( "; " ) ); .append( "; " ) );
attempt.messager.printMessage( attempt.messager.printMessage(
attempt.mappingMethod.getExecutable(), attempt.mappingMethod.getExecutable(),
attempt.positionHint, attempt.positionHint,
Message.GENERAL_AMBIGIOUS_MAPPING_METHODY_CONVERSIONX, Message.GENERAL_AMBIGUOUS_MAPPING_METHODY_CONVERSIONX,
attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(),
target.getName(), target.getName(),
result.toString() ); result.toString() );
@ -997,28 +1019,26 @@ public class MappingResolverImpl implements MappingResolver {
result = conversionRefY.assignment; result = conversionRefY.assignment;
} }
else { else {
reportAmbigiousError( yRefCandidates, targetType ); reportAmbiguousError( yRefCandidates, targetType );
} }
return this; return this;
} }
void reportAmbigiousError(Map<ConversionAssignment, List<SelectedMethod<T>>> yRefCandidates, Type target) { void reportAmbiguousError(Map<ConversionAssignment, List<SelectedMethod<T>>> yRefCandidates, Type target) {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
yRefCandidates.entrySet() yRefCandidates.entrySet()
.stream() .stream()
.limit( MAX_REPORTING_AMBIGUOUS )
.forEach( e -> result.append( "conversionY: " ) .forEach( e -> result.append( "conversionY: " )
.append( e.getKey().shortName() ) .append( e.getKey().shortName() )
.append( ", method(s)X: " ) .append( ", method(s)X: " )
.append( e.getValue() .append( attempt.join( e.getValue() ) )
.stream()
.map( v -> v.getMethod().shortName() )
.collect( Collectors.joining( ", " ) ) )
.append( "; " ) ); .append( "; " ) );
attempt.messager.printMessage( attempt.messager.printMessage(
attempt.mappingMethod.getExecutable(), attempt.mappingMethod.getExecutable(),
attempt.positionHint, attempt.positionHint,
Message.GENERAL_AMBIGIOUS_MAPPING_CONVERSIONY_METHODX, Message.GENERAL_AMBIGUOUS_MAPPING_CONVERSIONY_METHODX,
attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(), attempt.sourceRHS.getSourceType().getName() + " " + attempt.sourceRHS.getSourceParameterName(),
target.getName(), target.getName(),
result.toString() ); result.toString() );

View File

@ -7,6 +7,7 @@ package org.mapstruct.ap.internal.util;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import static org.mapstruct.ap.internal.util.MessageConstants.FAQ_AMBIGUOUS_URL;
import static org.mapstruct.ap.internal.util.MessageConstants.FAQ_QUALIFIER_URL; import static org.mapstruct.ap.internal.util.MessageConstants.FAQ_QUALIFIER_URL;
/** /**
@ -111,9 +112,9 @@ public enum Message {
GENERAL_NO_IMPLEMENTATION( "No implementation type is registered for return type %s." ), GENERAL_NO_IMPLEMENTATION( "No implementation type is registered for return type %s." ),
GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ), GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ),
GENERAL_AMBIGIOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s." ), GENERAL_AMBIGUOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ),
GENERAL_AMBIGIOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s." ), GENERAL_AMBIGUOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s. See " + FAQ_AMBIGUOUS_URL + " for more info." ),
GENERAL_AMBIGIOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ), GENERAL_AMBIGUOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ),
GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ), GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ),
GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ), GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ),
GENERAL_VALID_DATE( "Given date format \"%s\" is valid.", Diagnostic.Kind.NOTE ), GENERAL_VALID_DATE( "Given date format \"%s\" is valid.", Diagnostic.Kind.NOTE ),
@ -125,9 +126,9 @@ public enum Message {
GENERAL_NO_QUALIFYING_METHOD_NAMED( "Qualifier error. No method found annotated with @Named#value: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), GENERAL_NO_QUALIFYING_METHOD_NAMED( "Qualifier error. No method found annotated with @Named#value: [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ),
GENERAL_NO_QUALIFYING_METHOD_COMBINED( "Qualifier error. No method found annotated with @Named#value: [ %s ], annotated with [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ), GENERAL_NO_QUALIFYING_METHOD_COMBINED( "Qualifier error. No method found annotated with @Named#value: [ %s ], annotated with [ %s ]. See " + FAQ_QUALIFIER_URL + " for more info." ),
GENERAL_AMBIGIOUS_MAPPING_METHODY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( methodX ( parameter ) ): %s." ), GENERAL_AMBIGUOUS_MAPPING_METHODY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( methodX ( parameter ) ): %s." ),
GENERAL_AMBIGIOUS_MAPPING_CONVERSIONY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found conversionY( methodX ( parameter ) ): %s." ), GENERAL_AMBIGUOUS_MAPPING_CONVERSIONY_METHODX( "Ambiguous 2step methods found, mapping %s to %s. Found conversionY( methodX ( parameter ) ): %s." ),
GENERAL_AMBIGIOUS_MAPPING_METHODY_CONVERSIONX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( conversionX ( parameter ) ): %s." ), GENERAL_AMBIGUOUS_MAPPING_METHODY_CONVERSIONX( "Ambiguous 2step methods found, mapping %s to %s. Found methodY( conversionX ( parameter ) ): %s." ),
BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ), BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ),
BUILDER_NO_BUILD_METHOD_FOUND("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\".", Diagnostic.Kind.ERROR ), BUILDER_NO_BUILD_METHOD_FOUND("No build method \"%s\" found in \"%s\" for \"%s\". Found methods: \"%s\".", Diagnostic.Kind.ERROR ),

View File

@ -9,6 +9,7 @@ public final class MessageConstants {
public static final String AND = " and "; public static final String AND = " and ";
public static final String FAQ_QUALIFIER_URL = "https://mapstruct.org/faq/#qualifier"; public static final String FAQ_QUALIFIER_URL = "https://mapstruct.org/faq/#qualifier";
public static final String FAQ_AMBIGUOUS_URL = "https://mapstruct.org/faq/#ambiguous";
private MessageConstants() { private MessageConstants() {
} }

View File

@ -65,7 +65,8 @@ public class Issue1242Test {
".lang.Class<org.mapstruct.ap.test.bugs._1242.TargetB> clazz), org.mapstruct.ap.test.bugs._1242" + ".lang.Class<org.mapstruct.ap.test.bugs._1242.TargetB> clazz), org.mapstruct.ap.test.bugs._1242" +
".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB(@TargetType java.lang" + ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB(@TargetType java.lang" +
".Class<org.mapstruct.ap.test.bugs._1242.TargetB> clazz), org.mapstruct.ap.test.bugs._1242" + ".Class<org.mapstruct.ap.test.bugs._1242.TargetB> clazz), org.mapstruct.ap.test.bugs._1242" +
".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB().") ".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB()." +
" See https://mapstruct.org/faq/#ambiguous for more info." )
}) })
public void ambiguousMethodErrorForTwoFactoryMethodsWithSourceParam() { public void ambiguousMethodErrorForTwoFactoryMethodsWithSourceParam() {
} }

View File

@ -36,7 +36,8 @@ public class AmbiguousAnnotatedFactoryTest {
".ambiguousannotatedfactorymethod.Foo foo), org.mapstruct.ap.test.erroneous" + ".ambiguousannotatedfactorymethod.Foo foo), org.mapstruct.ap.test.erroneous" +
".ambiguousannotatedfactorymethod.Bar org.mapstruct.ap.test.erroneous" + ".ambiguousannotatedfactorymethod.Bar org.mapstruct.ap.test.erroneous" +
".ambiguousannotatedfactorymethod.AmbiguousBarFactory.createBar(org.mapstruct.ap.test.erroneous" + ".ambiguousannotatedfactorymethod.AmbiguousBarFactory.createBar(org.mapstruct.ap.test.erroneous" +
".ambiguousannotatedfactorymethod.Foo foo).") ".ambiguousannotatedfactorymethod.Foo foo)." +
" See https://mapstruct.org/faq/#ambiguous for more info.")
} }
) )

View File

@ -37,8 +37,8 @@ public class FactoryTest {
message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" + message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" +
".ambiguousfactorymethod.Bar: org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar " + ".ambiguousfactorymethod.Bar: org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar " +
"createBar(), org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar org.mapstruct.ap.test" + "createBar(), org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar org.mapstruct.ap.test" +
".erroneous.ambiguousfactorymethod.a.BarFactory.createBar().") ".erroneous.ambiguousfactorymethod.a.BarFactory.createBar()." +
" See https://mapstruct.org/faq/#ambiguous for more info.")
} }
) )
public void shouldUseTwoFactoryMethods() { public void shouldUseTwoFactoryMethods() {

View File

@ -0,0 +1,67 @@
/*
* 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.erroneous.ambiguousmapping;
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;
@IssueKey("2156")
@RunWith(AnnotationProcessorTestRunner.class)
public class AmbigiousMapperTest {
@Test
@WithClasses( ErroneousWithAmbiguousMethodsMapper.class)
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousWithAmbiguousMethodsMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 16,
message = "Ambiguous mapping methods found for mapping property "
+ "\"org.mapstruct.ap.test.erroneous.ambiguousmapping."
+ "ErroneousWithAmbiguousMethodsMapper.LeafDTO branch.leaf\" to "
+ "org.mapstruct.ap.test.erroneous.ambiguousmapping."
+ "ErroneousWithAmbiguousMethodsMapper.LeafEntity: "
+ "LeafEntity:map1(LeafDTO), LeafEntity:map2(LeafDTO). "
+ "See https://mapstruct.org/faq/#ambiguous for more info.")
}
)
public void testErrorMessageForAmbiguous() {
}
@Test
@WithClasses( ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class)
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 17,
message = "Ambiguous mapping methods found for mapping property "
+ "\"org.mapstruct.ap.test.erroneous.ambiguousmapping."
+ "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafDTO branch.leaf\" to "
+ "org.mapstruct.ap.test.erroneous.ambiguousmapping."
+ "ErroneousWithMoreThanFiveAmbiguousMethodsMapper.LeafEntity: "
+ "LeafEntity:map1(LeafDTO), "
+ "LeafEntity:map2(LeafDTO), "
+ "LeafEntity:map3(LeafDTO), "
+ "LeafEntity:map4(LeafDTO), "
+ "LeafEntity:map5(LeafDTO)"
+ "... and 1 more. "
+ "See https://mapstruct.org/faq/#ambiguous for more info.")
}
)
public void testErrorMessageForManyAmbiguous() {
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.erroneous.ambiguousmapping;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ErroneousWithAmbiguousMethodsMapper {
ErroneousWithAmbiguousMethodsMapper INSTANCE = Mappers.getMapper( ErroneousWithAmbiguousMethodsMapper.class );
TrunkEntity map(TrunkDTO dto);
default LeafEntity map1(LeafDTO dto) {
return new LeafEntity();
}
// duplicated method, triggering ambigious mapping method
default LeafEntity map2(LeafDTO dto) {
return new LeafEntity();
}
// CHECKSTYLE:OFF
class TrunkDTO {
public BranchDTO branch;
}
class BranchDTO {
public LeafDTO leaf;
}
class LeafDTO {
public int numberOfVeigns;
}
class TrunkEntity {
public BranchEntity branch;
}
class BranchEntity {
public LeafEntity leaf;
}
class LeafEntity {
public int numberOfVeigns;
}
// CHECKSTYLE ON
}

View File

@ -0,0 +1,74 @@
/*
* 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.erroneous.ambiguousmapping;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ErroneousWithMoreThanFiveAmbiguousMethodsMapper {
ErroneousWithMoreThanFiveAmbiguousMethodsMapper
INSTANCE = Mappers.getMapper( ErroneousWithMoreThanFiveAmbiguousMethodsMapper.class );
TrunkEntity map(TrunkDTO dto);
default LeafEntity map1(LeafDTO dto) {
return new LeafEntity();
}
// duplicated method, triggering ambigious mapping method
default LeafEntity map2(LeafDTO dto) {
return new LeafEntity();
}
// duplicated method, triggering ambigious mapping method
default LeafEntity map3(LeafDTO dto) {
return new LeafEntity();
}
// duplicated method, triggering ambigious mapping method
default LeafEntity map4(LeafDTO dto) {
return new LeafEntity();
}
// duplicated method, triggering ambigious mapping method
default LeafEntity map5(LeafDTO dto) {
return new LeafEntity();
}
// duplicated method, triggering ambigious mapping method
default LeafEntity map6(LeafDTO dto) {
return new LeafEntity();
}
// CHECKSTYLE:OFF
class TrunkDTO {
public BranchDTO branch;
}
class BranchDTO {
public LeafDTO leaf;
}
class LeafDTO {
public int numberOfVeigns;
}
class TrunkEntity {
public BranchEntity branch;
}
class BranchEntity {
public LeafEntity leaf;
}
class LeafEntity {
public int numberOfVeigns;
}
// CHECKSTYLE ON
}

View File

@ -69,13 +69,12 @@ public class ComplexInheritanceTest {
kind = Kind.ERROR, kind = Kind.ERROR,
type = ErroneousSourceCompositeTargetCompositeMapper.class, type = ErroneousSourceCompositeTargetCompositeMapper.class,
line = 19, line = 19,
message = "Ambiguous mapping methods found for mapping property \"org.mapstruct.ap.test.inheritance" + message = "Ambiguous mapping methods found for mapping property "
".complex.SourceExt prop1\" to org.mapstruct.ap.test.inheritance.complex.Reference: org.mapstruct.ap" + + "\"org.mapstruct.ap.test.inheritance.complex.SourceExt prop1\" "
".test.inheritance.complex.Reference org.mapstruct.ap.test.inheritance.complex" + + "to org.mapstruct.ap.test.inheritance.complex.Reference: "
".AdditionalMappingHelper.asReference(org.mapstruct.ap.test.inheritance.complex.SourceBase source), " + + "Reference:asReference(SourceBase), Reference:asReference(AdditionalFooSource). "
"org.mapstruct.ap.test.inheritance.complex.Reference org.mapstruct.ap.test.inheritance.complex" + + "See https://mapstruct.org/faq/#ambiguous for more info.")
".AdditionalMappingHelper.asReference(org.mapstruct.ap.test.inheritance.complex.AdditionalFooSource " + )
"source)."))
public void ambiguousMappingMethodsReportError() { public void ambiguousMappingMethodsReportError() {
} }

View File

@ -48,7 +48,9 @@ public class InheritanceSelectionTest {
message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.selection.resulttype" + message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.selection.resulttype" +
".Fruit: org.mapstruct.ap.test.selection.resulttype.Apple org.mapstruct.ap.test.selection" + ".Fruit: org.mapstruct.ap.test.selection.resulttype.Apple org.mapstruct.ap.test.selection" +
".resulttype.ConflictingFruitFactory.createApple(), org.mapstruct.ap.test.selection.resulttype" + ".resulttype.ConflictingFruitFactory.createApple(), org.mapstruct.ap.test.selection.resulttype" +
".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana().") ".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana()." +
" See https://mapstruct.org/faq/#ambiguous for more info."
)
} }
) )
public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() { public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() {