#2629 Use ModuleElement when getting type element

Prior to this MapStruct would only use `Elements#getTypeElement`.
With this PR if the mapper being generated is within a module MapStruct will use that module
for the methods that are needed (getTypeElement and getPackageElement).

Adapt the build to require a minimum Java 11 for building the processor module.
Adapt the GitHub actions to properly run integration tests with Java 8
Ignore Java 9 usages for the animal-sniffer-plugin
This commit is contained in:
Filip Hrisafov 2022-01-30 20:52:22 +01:00 committed by GitHub
parent 37835a5607
commit 2a2c11e871
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 357 additions and 24 deletions

View File

@ -12,7 +12,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
java: [11, 13, 16, 17] java: [13, 16, 17]
name: 'Linux JDK ${{ matrix.java }}' name: 'Linux JDK ${{ matrix.java }}'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -25,15 +25,15 @@ jobs:
- name: 'Test' - name: 'Test'
run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true
linux: linux:
name: 'Linux JDK 8' name: 'Linux JDK 11'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: 'Checkout' - name: 'Checkout'
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: 'Set up JDK 8' - name: 'Set up JDK 11'
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 8 java-version: 11
- name: 'Test' - name: 'Test'
run: ./mvnw ${MAVEN_ARGS} install run: ./mvnw ${MAVEN_ARGS} install
- name: 'Generate coverage report' - name: 'Generate coverage report'
@ -43,25 +43,43 @@ jobs:
- name: 'Publish Snapshots' - name: 'Publish Snapshots'
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'mapstruct/mapstruct' if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'mapstruct/mapstruct'
run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy
linux-jdk-8:
name: 'Linux JDK 8'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v2
- name: 'Set up JDK 11 for building everything'
uses: actions/setup-java@v1
with:
java-version: 11
- name: 'Install Processor'
run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am
- name: 'Set up JDK 8 for running integration tests'
uses: actions/setup-java@v1
with:
java-version: 8
- name: 'Run integration tests'
run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest
windows: windows:
name: 'Windows' name: 'Windows'
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: 'Set up JDK 8' - name: 'Set up JDK 11'
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 8 java-version: 11
- name: 'Test' - name: 'Test'
run: ./mvnw ${MAVEN_ARGS} install run: ./mvnw %MAVEN_ARGS% install
mac: mac:
name: 'Mac OS' name: 'Mac OS'
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: 'Set up JDK 8' - name: 'Set up JDK 11'
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 8 java-version: 11
- name: 'Test' - name: 'Test'
run: ./mvnw ${MAVEN_ARGS} install run: ./mvnw ${MAVEN_ARGS} install

View File

@ -85,6 +85,14 @@ public class MavenIntegrationTest {
void lombokBuilderTest() { void lombokBuilderTest() {
} }
@ProcessorTest(baseDir = "lombokModuleTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC,
ProcessorTest.ProcessorType.JAVAC_WITH_PATHS
})
@EnabledForJreRange(min = JRE.JAVA_11)
void lombokModuleTest() {
}
@ProcessorTest(baseDir = "namingStrategyTest", processorTypes = { @ProcessorTest(baseDir = "namingStrategyTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC ProcessorTest.ProcessorType.JAVAC
}) })

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>lombokModuleIntegrationTest</artifactId>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>generate-via-compiler-plugin-with-annotation-processor-paths</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgument combine.self="override"></compilerArgument>
<compilerId>\${compiler-id}</compilerId>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>${project.groupId}</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
<dependencies>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId>
<version>${org.eclipse.tycho.compiler-jdt.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,9 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
module org.example {
requires org.mapstruct;
requires lombok;
}

View File

@ -0,0 +1,18 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.lombok;
public class Address {
private final String addressLine;
public Address(String addressLine) {
this.addressLine = addressLine;
}
public String getAddressLine() {
return addressLine;
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.lombok;
public class AddressDto {
private final String addressLine;
public AddressDto(String addressLine) {
this.addressLine = addressLine;
}
public String getAddressLine() {
return addressLine;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.lombok;
public class Person {
private final String name;
private final int age;
private final Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Address getAddress() {
return address;
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.lombok;
public class PersonDto {
private final String name;
private final int age;
private final AddressDto address;
public PersonDto(String name, int age, AddressDto address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public AddressDto getAddress() {
return address;
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.lombok;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
/**
*/
@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
Person fromDto(PersonDto personDto);
PersonDto toDto(Person personDto);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.itest.lombok;
import org.junit.Test;
import org.mapstruct.factory.Mappers;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for generation of Lombok Builder Mapper implementations
*
* @author Eric Martineau
*/
public class LombokMapperTest {
@Test
public void testSimpleImmutableBuilderHappyPath() {
PersonDto personDto = PersonMapper.INSTANCE.toDto( new Person( "Bob", 33, new Address( "Wild Drive" ) ) );
assertThat( personDto.getAge() ).isEqualTo( 33 );
assertThat( personDto.getName() ).isEqualTo( "Bob" );
assertThat( personDto.getAddress() ).isNotNull();
assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "Wild Drive" );
}
@Test
public void testLombokToImmutable() {
Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) );
assertThat( person.getAge() ).isEqualTo( 33 );
assertThat( person.getName() ).isEqualTo( "Bob" );
assertThat( person.getAddress() ).isNotNull();
assertThat( person.getAddress().getAddressLine() ).isEqualTo( "Wild Drive" );
}
}

View File

@ -36,6 +36,11 @@
<!-- automatically run annotation processors within the incremental compilation --> <!-- automatically run annotation processors within the incremental compilation -->
<!-- Needed for the gem tools processor--> <!-- Needed for the gem tools processor-->
<m2e.apt.activation>jdt_apt</m2e.apt.activation> <m2e.apt.activation>jdt_apt</m2e.apt.activation>
<!--
The minimum Java Version that is needed to build a module in MapStruct.
The processor module needs at least Java 11.
-->
<minimum.java.version>1.8</minimum.java.version>
</properties> </properties>
<licenses> <licenses>
@ -214,7 +219,7 @@
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.20</version> <version>1.18.22</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.immutables</groupId> <groupId>org.immutables</groupId>
@ -666,6 +671,9 @@
<artifactId>java18</artifactId> <artifactId>java18</artifactId>
<version>1.0</version> <version>1.0</version>
</signature> </signature>
<annotations>
<annotation>org.mapstruct.ap.internal.util.IgnoreJRERequirement</annotation>
</annotations>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -685,7 +693,7 @@
<configuration> <configuration>
<rules> <rules>
<requireJavaVersion> <requireJavaVersion>
<version>[1.8,)</version> <version>[${minimum.java.version},)</version>
</requireJavaVersion> </requireJavaVersion>
<DependencyConvergence /> <DependencyConvergence />
<requirePluginVersions /> <requirePluginVersions />

View File

@ -24,6 +24,7 @@
<!-- Netbeans has a problem when we use late binding with @ in the surefire arg line. <!-- Netbeans has a problem when we use late binding with @ in the surefire arg line.
Therefore we set this empty property here--> Therefore we set this empty property here-->
<jacocoArgLine /> <jacocoArgLine />
<minimum.java.version>11</minimum.java.version>
</properties> </properties>
<dependencies> <dependencies>

View File

@ -269,7 +269,11 @@ public class MappingProcessor extends AbstractProcessor {
// of one outer interface // of one outer interface
List<? extends Element> tst = mapperElement.getEnclosedElements(); List<? extends Element> tst = mapperElement.getEnclosedElements();
ProcessorContext context = new DefaultModelElementProcessorContext( ProcessorContext context = new DefaultModelElementProcessorContext(
processingEnv, options, roundContext, getDeclaredTypesNotToBeImported( mapperElement ) processingEnv,
options,
roundContext,
getDeclaredTypesNotToBeImported( mapperElement ),
mapperElement
); );
processMapperTypeElement( context, mapperElement ); processMapperTypeElement( context, mapperElement );

View File

@ -13,6 +13,7 @@ import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.common.TypeFactory;
@ -46,14 +47,14 @@ public class DefaultModelElementProcessorContext implements ProcessorContext {
private final RoundContext roundContext; private final RoundContext roundContext;
public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options, public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options,
RoundContext roundContext, Map<String, String> notToBeImported) { RoundContext roundContext, Map<String, String> notToBeImported, TypeElement mapperElement) {
this.processingEnvironment = processingEnvironment; this.processingEnvironment = processingEnvironment;
this.messager = new DelegatingMessager( processingEnvironment.getMessager(), options.isVerbose() ); this.messager = new DelegatingMessager( processingEnvironment.getMessager(), options.isVerbose() );
this.accessorNaming = roundContext.getAnnotationProcessorContext().getAccessorNaming(); this.accessorNaming = roundContext.getAnnotationProcessorContext().getAccessorNaming();
this.versionInformation = DefaultVersionInformation.fromProcessingEnvironment( processingEnvironment ); this.versionInformation = DefaultVersionInformation.fromProcessingEnvironment( processingEnvironment );
this.delegatingTypes = TypeUtils.create( processingEnvironment, versionInformation ); this.delegatingTypes = TypeUtils.create( processingEnvironment, versionInformation );
this.delegatingElements = ElementUtils.create( processingEnvironment, versionInformation ); this.delegatingElements = ElementUtils.create( processingEnvironment, versionInformation, mapperElement );
this.roundContext = roundContext; this.roundContext = roundContext;
this.typeFactory = new TypeFactory( this.typeFactory = new TypeFactory(
delegatingElements, delegatingElements,

View File

@ -13,11 +13,13 @@ import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.Name; import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement; import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
@ -35,19 +37,44 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
public abstract class AbstractElementUtilsDecorator implements ElementUtils { public abstract class AbstractElementUtilsDecorator implements ElementUtils {
private final Elements delegate; private final Elements delegate;
/**
* The module element when running with the module system,
* {@code null} otherwise.
*/
private final Element moduleElement;
AbstractElementUtilsDecorator(ProcessingEnvironment processingEnv) { @IgnoreJRERequirement
AbstractElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) {
this.delegate = processingEnv.getElementUtils(); this.delegate = processingEnv.getElementUtils();
if ( SourceVersion.RELEASE_8.compareTo( processingEnv.getSourceVersion() ) >= 0 ) {
// We are running with Java 8 or lower
this.moduleElement = null;
}
else {
this.moduleElement = this.delegate.getModuleOf( mapperElement );
}
} }
@Override @Override
@IgnoreJRERequirement
public PackageElement getPackageElement(CharSequence name) { public PackageElement getPackageElement(CharSequence name) {
return delegate.getPackageElement( name ); if ( this.moduleElement == null ) {
return this.delegate.getPackageElement( name );
}
// If the module element is not null then we must be running on Java 8+
return this.delegate.getPackageElement( (ModuleElement) moduleElement, name );
} }
@Override @Override
@IgnoreJRERequirement
public TypeElement getTypeElement(CharSequence name) { public TypeElement getTypeElement(CharSequence name) {
return delegate.getTypeElement( name ); if ( this.moduleElement == null ) {
return this.delegate.getTypeElement( name );
}
// If the module element is not null then we must be running on Java 8+
return this.delegate.getTypeElement( (ModuleElement) moduleElement, name );
} }
@Override @Override

View File

@ -13,8 +13,8 @@ public class EclipseElementUtilsDecorator extends AbstractElementUtilsDecorator
private final Elements delegate; private final Elements delegate;
EclipseElementUtilsDecorator(ProcessingEnvironment processingEnv) { EclipseElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) {
super( processingEnv ); super( processingEnv, mapperElement );
this.delegate = processingEnv.getElementUtils(); this.delegate = processingEnv.getElementUtils();
} }

View File

@ -16,12 +16,13 @@ import org.mapstruct.ap.internal.version.VersionInformation;
public interface ElementUtils extends Elements { public interface ElementUtils extends Elements {
static ElementUtils create(ProcessingEnvironment processingEnvironment, VersionInformation info ) { static ElementUtils create(ProcessingEnvironment processingEnvironment, VersionInformation info,
TypeElement mapperElement) {
if ( info.isEclipseJDTCompiler() ) { if ( info.isEclipseJDTCompiler() ) {
return new EclipseElementUtilsDecorator( processingEnvironment ); return new EclipseElementUtilsDecorator( processingEnvironment, mapperElement );
} }
else { else {
return new JavacElementUtilsDecorator( processingEnvironment ); return new JavacElementUtilsDecorator( processingEnvironment, mapperElement );
} }
} }

View File

@ -0,0 +1,16 @@
/*
* 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.internal.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE })
public @interface IgnoreJRERequirement {
}

View File

@ -10,8 +10,8 @@ import javax.lang.model.element.TypeElement;
public class JavacElementUtilsDecorator extends AbstractElementUtilsDecorator { public class JavacElementUtilsDecorator extends AbstractElementUtilsDecorator {
JavacElementUtilsDecorator(ProcessingEnvironment processingEnv) { JavacElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) {
super( processingEnv ); super( processingEnv, mapperElement );
} }
@Override @Override