Skip to content

Commit 211c627

Browse files
committed
mapstruct#1751 Fix handling of possible builder creation methods with generic signature
When a method has a generic signature and the builder type is generic then the method return type does not match the builder type. Therefore check only for raw types. Add extra check for void method since a void method can't be a builder creation method
1 parent 69d0a2d commit 211c627

6 files changed

Lines changed: 151 additions & 1 deletion

File tree

processor/src/main/java/org/mapstruct/ap/spi/DefaultBuilderProvider.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,14 @@ protected boolean isPossibleBuilderCreationMethod(ExecutableElement method, Type
204204
return method.getParameters().isEmpty()
205205
&& method.getModifiers().contains( Modifier.PUBLIC )
206206
&& method.getModifiers().contains( Modifier.STATIC )
207-
&& !typeUtils.isSameType( method.getReturnType(), typeElement.asType() );
207+
&& method.getReturnType().getKind() != TypeKind.VOID
208+
// Only compare raw elements
209+
// Reason: if the method is a generic method (<T> Holder<T> build()) and the type element is (Holder<T>)
210+
// then the return type of the method does not match the type of the type element
211+
&& !typeUtils.isSameType(
212+
typeUtils.erasure( method.getReturnType() ),
213+
typeUtils.erasure( typeElement.asType() )
214+
);
208215
}
209216

210217
/**
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._1751;
7+
8+
/**
9+
* @author Filip Hrisafov
10+
*/
11+
public class Holder<T> {
12+
13+
private final T value;
14+
15+
public Holder(T value) {
16+
this.value = value;
17+
}
18+
19+
public T getValue() {
20+
return value;
21+
}
22+
23+
// If empty is considered as a builder creation method, this method would be the build method and would
24+
// lead to a stackoverflow
25+
@SuppressWarnings("unused")
26+
public Holder<T> duplicate() {
27+
return new Holder<>( value );
28+
}
29+
30+
// This method should not be considered as builder creation method
31+
@SuppressWarnings("unused")
32+
public static <V> Holder<V> empty() {
33+
return new Holder<>( null );
34+
}
35+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._1751;
7+
8+
import org.mapstruct.Mapper;
9+
import org.mapstruct.factory.Mappers;
10+
11+
/**
12+
* @author Filip Hrisafov
13+
*/
14+
@Mapper
15+
public interface Issue1751Mapper {
16+
17+
Issue1751Mapper INSTANCE = Mappers.getMapper( Issue1751Mapper.class );
18+
19+
Target map(Source source);
20+
21+
default Holder<Target> mapToHolder(Source source) {
22+
return new Holder<>( this.map( source ) );
23+
}
24+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._1751;
7+
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import org.mapstruct.ap.testutil.IssueKey;
11+
import org.mapstruct.ap.testutil.WithClasses;
12+
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
/**
17+
* @author Filip Hrisafov
18+
*/
19+
@IssueKey("1772")
20+
@RunWith(AnnotationProcessorTestRunner.class)
21+
@WithClasses({
22+
Holder.class,
23+
Issue1751Mapper.class,
24+
Source.class,
25+
Target.class
26+
})
27+
public class Issue1751Test {
28+
29+
@Test
30+
public void name() {
31+
Source source = new Source();
32+
source.setValue( "some value" );
33+
34+
Holder<Target> targetHolder = Issue1751Mapper.INSTANCE.mapToHolder( source );
35+
36+
assertThat( targetHolder.getValue() )
37+
.extracting( Target::getValue )
38+
.isEqualTo( "some value" );
39+
}
40+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._1751;
7+
8+
/**
9+
* @author Filip Hrisafov
10+
*/
11+
public class Source {
12+
13+
private String value;
14+
15+
public String getValue() {
16+
return value;
17+
}
18+
19+
public void setValue(String value) {
20+
this.value = value;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.bugs._1751;
7+
8+
/**
9+
* @author Filip Hrisafov
10+
*/
11+
public class Target {
12+
13+
private String value;
14+
15+
public String getValue() {
16+
return value;
17+
}
18+
19+
public void setValue(String value) {
20+
this.value = value;
21+
}
22+
}

0 commit comments

Comments
 (0)