#2463 Selection stops when method type-args are flipped (#2487)

This commit is contained in:
Sjaak Derksen 2021-06-19 13:56:26 +02:00 committed by GitHub
parent 55c62ab43f
commit 7f38efad4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 188 additions and 8 deletions

View File

@ -248,10 +248,12 @@ public class MethodMatcher {
// e.g. <T> String method( List<? extends T> in ) // e.g. <T> String method( List<? extends T> in )
Type.ResolvedPair resolved = mthdParType.resolveParameterToType( matchingType, aCandidateMethodType ); Type.ResolvedPair resolved = mthdParType.resolveParameterToType( matchingType, aCandidateMethodType );
if ( resolved == null ) { if ( resolved.getMatch() == null ) {
// cannot find a candidate type, but should have since the typeFromCandidateMethod had parameters // we should be dealing with something containing a type parameter at this point. This is
// to be resolved // covered with the checks above. Therefore resolved itself cannot be null.
return !hasGenericTypeParameters( aCandidateMethodType ); // If there is no match here, continue with the next candidate, perhaps there will a match with
// the next method type parameter
continue;
} }
// resolved something at this point, a candidate can be fetched or created // resolved something at this point, a candidate can be fetched or created
@ -270,13 +272,10 @@ public class MethodMatcher {
// it might be already set, but we just checked if its an equivalent type // it might be already set, but we just checked if its an equivalent type
if ( typeVarCandidate.match == null ) { if ( typeVarCandidate.match == null ) {
typeVarCandidate.match = resolved.getMatch(); typeVarCandidate.match = resolved.getMatch();
if ( typeVarCandidate.match == null) {
return false;
}
typeVarCandidate.pairs.add( resolved ); typeVarCandidate.pairs.add( resolved );
} }
else if ( !areEquivalent( resolved.getMatch(), typeVarCandidate.match ) ) { else if ( !areEquivalent( resolved.getMatch(), typeVarCandidate.match ) ) {
// type has been resolved twice, but with a different candidate (conflict) // type has been resolved twice, but with a different candidate (conflict). Stop
return false; return false;
} }

View File

@ -50,6 +50,22 @@ public class MultipleTypeVarTest {
assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) ); assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) );
} }
@ProcessorTest
@WithClasses( ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.class )
public void testGenericReturnTypeVarBothGenericArgumentsFlipped() {
ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Pair pair
= new ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Pair( "test", 5L );
ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Source src =
new ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Source( pair );
ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Target target =
ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.INSTANCE.toTarget( src );
assertThat( target ).isNotNull();
assertThat( target.getProp() ).isNotNull();
assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) );
}
@ProcessorTest @ProcessorTest
@WithClasses( SourceTypeHasMultipleTypeVarBothGenericMapper.class ) @WithClasses( SourceTypeHasMultipleTypeVarBothGenericMapper.class )
public void testGenericSourceTypeVarBothGeneric() { public void testGenericSourceTypeVarBothGeneric() {

View File

@ -0,0 +1,76 @@
/*
* 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.selection.methodgenerics.multiple;
import java.util.HashMap;
import java.util.Map;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper {
ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper INSTANCE =
Mappers.getMapper( ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.class );
Target toTarget(Source source);
default <U, T> HashMap<T, U> toMap( Pair<T, U> entry) {
HashMap<T, U> result = new HashMap<>( );
result.put( entry.first, entry.second );
return result;
}
class Source {
private Pair<String, Long> prop;
public Source(Pair<String, Long> prop) {
this.prop = prop;
}
public Pair<String, Long> getProp() {
return prop;
}
}
class Target {
private Map<String, Long> prop;
public Map<String, Long> getProp() {
return prop;
}
public Target setProp(Map<String, Long> prop) {
this.prop = prop;
return this;
}
}
class Pair<T, U> {
private final T first;
private final U second;
public Pair(T first, U second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public U getSecond() {
return second;
}
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.selection.methodgenerics.objectfactory;
import org.mapstruct.Mapper;
import org.mapstruct.ObjectFactory;
import org.mapstruct.TargetType;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ObjectFactoryMapper {
ObjectFactoryMapper INSTANCE = Mappers.getMapper( ObjectFactoryMapper.class );
TargetA toTarget(SourceA source);
@ObjectFactory
default <T extends Target, S extends Source> T createTarget(S source, @TargetType Class<T> targetType) {
if ( source.isA() ) {
return (T) new TargetA();
}
return (T) new TargetB();
}
abstract class Source {
public abstract boolean isA();
}
class SourceA extends Source {
@Override
public boolean isA() {
return true;
}
}
class SourceB extends Source {
@Override
public boolean isA() {
return false;
}
}
abstract class Target {
}
class TargetA extends Target {
private TargetA() {
}
}
class TargetB extends Target {
private TargetB() {
}
}
}

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.selection.methodgenerics.objectfactory;
import static org.assertj.core.api.Assertions.assertThat;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
public class ObjectFactoryTest {
@IssueKey( "2463" )
@ProcessorTest
@WithClasses( ObjectFactoryMapper.class )
public void testSelectionOfFactoryMethod() {
ObjectFactoryMapper.SourceA source = new ObjectFactoryMapper.SourceA();
ObjectFactoryMapper.Target target = ObjectFactoryMapper.INSTANCE.toTarget( source );
assertThat( target ).isNotNull();
assertThat( target ).isInstanceOf( ObjectFactoryMapper.TargetA.class );
}
}