Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -148,6 +149,24 @@ public final Object[] getVariants() {
@Nullable
abstract PsiType resolvedType();

/**
* Substitutes type parameters in {@code memberType} using the generic context from the previous reference in the
* property chain.
*/
@Nullable
protected PsiType substituteMemberType(@Nullable PsiType memberType) {
if ( memberType == null || previous == null ) {
return memberType;
}

PsiType ownerType = previous.resolvedType();
if ( ownerType == null ) {
return memberType;
}

return PsiUtil.resolveGenericsClassInType( ownerType ).getSubstitutor().substitute( memberType );
}

@Override
public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
PsiElement reference = resolve();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,16 @@ PsiType resolvedType() {
PsiElement element = resolve();

if ( element instanceof PsiMethod psiMethod ) {
return psiMethod.getReturnType();
return substituteMemberType( psiMethod.getReturnType() );
}
else if ( element instanceof PsiParameter psiParameter ) {
return psiParameter.getType();
}
else if ( element instanceof PsiRecordComponent psiRecordComponent ) {
return psiRecordComponent.getType();
return substituteMemberType( psiRecordComponent.getType() );
}
else if ( element instanceof PsiField psiField ) {
return psiField.getType();
return substituteMemberType( psiField.getType() );
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,16 @@ PsiType resolvedType() {
PsiElement element = resolve();

if ( element instanceof PsiMethod psiMethod ) {
return firstParameterPsiType( psiMethod );
return substituteMemberType( firstParameterPsiType( psiMethod ) );
}
else if ( element instanceof PsiParameter psiParameter ) {
return psiParameter.getType();
}
else if ( element instanceof PsiRecordComponent psiRecordComponent ) {
return psiRecordComponent.getType();
return substituteMemberType( psiRecordComponent.getType() );
}
else if ( element instanceof PsiField psiField ) {
return psiField.getType();
return substituteMemberType( psiField.getType() );
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
*/
package org.mapstruct.intellij;

import java.io.File;

import com.intellij.codeInsight.completion.LightFixtureCompletionTestCase;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;

import java.io.File;

/**
* Base completion test case for MapStruct.
*
Expand All @@ -29,7 +28,13 @@ protected void setUp() throws Exception {
super.setUp();
final String mapstructLibPath = PathUtil.toSystemIndependentName( new File( BUILD_LIBS_DIRECTORY )
.getAbsolutePath() );
VfsRootAccess.allowRootAccess( getTestRootDisposable(), mapstructLibPath );

allowAccessToDirsIfExists(
BUILD_LIBS_DIRECTORY,
"testData",
"build/test-libs"
);

PsiTestUtil.addLibrary(
myFixture.getProjectDisposable(),
myFixture.getModule(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,36 @@ public void testFluentGenericTargetMapper() {
);
}

public void testGenericCarWrapperSourceAutoCompleteAfterCar() {
configureByTestName();

assertThat( myItems )
.extracting( LookupElement::getLookupString )
.containsExactlyInAnyOrder(
"winCode"
);

assertThat( myItems )
.extracting( LookupElementPresentation::renderElement )
.usingRecursiveFieldByFieldElementComparator()
.containsExactlyInAnyOrder(
createVariable( "winCode", "String" )
);
}

public void testGenericCarWrapperTargetAutoCompleteAfterCar() {
configureByTestName();

assertThat( myItems )
.extracting( LookupElement::getLookupString )
.containsExactlyInAnyOrder( "winCode" );

assertThat( myItems )
.extracting( LookupElementPresentation::renderElement )
.usingRecursiveFieldByFieldElementComparator()
.containsExactlyInAnyOrder( createVariable( "winCode", "String" ) );
}

public void testVariantsCarMapperNoSourceClass() {
myFixture.configureByFile( "CarMapperNoSourceClass.java" );
complete();
Expand Down
56 changes: 56 additions & 0 deletions testData/mapping/GenericCarWrapperSourceAutoCompleteAfterCar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.complex;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper
public interface GenericCarWrapperMapper {

@Mapping(target = "id", source = "wrapper.car.<caret>winCode")
CarEntity toCarDto(CarWrapper<Car> wrapper);
}

class CarEntity {

private String id;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

}

class Car {

private String winCode;

public String getWinCode() {
return winCode;
}

public void setWinCode(String winCode) {
this.winCode = winCode;
}
}

class CarWrapper<T> {

private T car;

public T getCar() {
return car;
}

public void setCar(T car) {
this.car = car;
}
}
56 changes: 56 additions & 0 deletions testData/mapping/GenericCarWrapperTargetAutoCompleteAfterCar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.complex;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

@Mapper
public interface GenericCarWrapperTargetMapper {

@Mapping(source = "id", target = "wrapper.car.<caret>winCode")
void update(@MappingTarget CarWrapper<Car> wrapper, CarEntity entity);
}

class CarEntity {

private String id;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}
}

class Car {

private String winCode;

public String getWinCode() {
return winCode;
}

public void setWinCode(String winCode) {
this.winCode = winCode;
}
}

class CarWrapper<T> {

private T car;

public T getCar() {
return car;
}

public void setCar(T car) {
this.car = car;
}
}