File tree Expand file tree Collapse file tree
main/java/org/mapstruct/intellij/codeinsight/references
test/java/org/mapstruct/intellij Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1818import com .intellij .psi .PsiReferenceExpression ;
1919import com .intellij .psi .PsiType ;
2020import com .intellij .psi .util .PsiTreeUtil ;
21+ import com .intellij .psi .util .PsiUtil ;
2122import com .intellij .util .IncorrectOperationException ;
2223import org .jetbrains .annotations .NotNull ;
2324import org .jetbrains .annotations .Nullable ;
@@ -148,6 +149,24 @@ public final Object[] getVariants() {
148149 @ Nullable
149150 abstract PsiType resolvedType ();
150151
152+ /**
153+ * Substitutes type parameters in {@code memberType} using the generic context from the previous reference in the
154+ * property chain.
155+ */
156+ @ Nullable
157+ protected PsiType substituteMemberType (@ Nullable PsiType memberType ) {
158+ if ( memberType == null || previous == null ) {
159+ return memberType ;
160+ }
161+
162+ PsiType ownerType = previous .resolvedType ();
163+ if ( ownerType == null ) {
164+ return memberType ;
165+ }
166+
167+ return PsiUtil .resolveGenericsClassInType ( ownerType ).getSubstitutor ().substitute ( memberType );
168+ }
169+
151170 @ Override
152171 public PsiElement handleElementRename (@ NotNull String newElementName ) throws IncorrectOperationException {
153172 PsiElement reference = resolve ();
Original file line number Diff line number Diff line change @@ -135,16 +135,16 @@ PsiType resolvedType() {
135135 PsiElement element = resolve ();
136136
137137 if ( element instanceof PsiMethod psiMethod ) {
138- return psiMethod .getReturnType ();
138+ return substituteMemberType ( psiMethod .getReturnType () );
139139 }
140140 else if ( element instanceof PsiParameter psiParameter ) {
141141 return psiParameter .getType ();
142142 }
143143 else if ( element instanceof PsiRecordComponent psiRecordComponent ) {
144- return psiRecordComponent .getType ();
144+ return substituteMemberType ( psiRecordComponent .getType () );
145145 }
146146 else if ( element instanceof PsiField psiField ) {
147- return psiField .getType ();
147+ return substituteMemberType ( psiField .getType () );
148148 }
149149
150150 return null ;
Original file line number Diff line number Diff line change @@ -205,16 +205,16 @@ PsiType resolvedType() {
205205 PsiElement element = resolve ();
206206
207207 if ( element instanceof PsiMethod psiMethod ) {
208- return firstParameterPsiType ( psiMethod );
208+ return substituteMemberType ( firstParameterPsiType ( psiMethod ) );
209209 }
210210 else if ( element instanceof PsiParameter psiParameter ) {
211211 return psiParameter .getType ();
212212 }
213213 else if ( element instanceof PsiRecordComponent psiRecordComponent ) {
214- return psiRecordComponent .getType ();
214+ return substituteMemberType ( psiRecordComponent .getType () );
215215 }
216216 else if ( element instanceof PsiField psiField ) {
217- return psiField .getType ();
217+ return substituteMemberType ( psiField .getType () );
218218 }
219219
220220 return null ;
Original file line number Diff line number Diff line change 55 */
66package org .mapstruct .intellij ;
77
8- import java .io .File ;
9-
108import com .intellij .codeInsight .completion .LightFixtureCompletionTestCase ;
119import com .intellij .openapi .util .text .StringUtil ;
12- import com .intellij .openapi .vfs .newvfs .impl .VfsRootAccess ;
1310import com .intellij .testFramework .LightProjectDescriptor ;
1411import com .intellij .testFramework .PsiTestUtil ;
1512import com .intellij .util .PathUtil ;
1613import org .jetbrains .annotations .NotNull ;
1714
15+ import java .io .File ;
16+
1817/**
1918 * Base completion test case for MapStruct.
2019 *
@@ -29,7 +28,13 @@ protected void setUp() throws Exception {
2928 super .setUp ();
3029 final String mapstructLibPath = PathUtil .toSystemIndependentName ( new File ( BUILD_LIBS_DIRECTORY )
3130 .getAbsolutePath () );
32- VfsRootAccess .allowRootAccess ( getTestRootDisposable (), mapstructLibPath );
31+
32+ allowAccessToDirsIfExists (
33+ BUILD_LIBS_DIRECTORY ,
34+ "testData" ,
35+ "build/test-libs"
36+ );
37+
3338 PsiTestUtil .addLibrary (
3439 myFixture .getProjectDisposable (),
3540 myFixture .getModule (),
Original file line number Diff line number Diff line change @@ -488,6 +488,36 @@ public void testFluentGenericTargetMapper() {
488488 );
489489 }
490490
491+ public void testGenericCarWrapperSourceAutoCompleteAfterCar () {
492+ configureByTestName ();
493+
494+ assertThat ( myItems )
495+ .extracting ( LookupElement ::getLookupString )
496+ .containsExactlyInAnyOrder (
497+ "winCode"
498+ );
499+
500+ assertThat ( myItems )
501+ .extracting ( LookupElementPresentation ::renderElement )
502+ .usingRecursiveFieldByFieldElementComparator ()
503+ .containsExactlyInAnyOrder (
504+ createVariable ( "winCode" , "String" )
505+ );
506+ }
507+
508+ public void testGenericCarWrapperTargetAutoCompleteAfterCar () {
509+ configureByTestName ();
510+
511+ assertThat ( myItems )
512+ .extracting ( LookupElement ::getLookupString )
513+ .containsExactlyInAnyOrder ( "winCode" );
514+
515+ assertThat ( myItems )
516+ .extracting ( LookupElementPresentation ::renderElement )
517+ .usingRecursiveFieldByFieldElementComparator ()
518+ .containsExactlyInAnyOrder ( createVariable ( "winCode" , "String" ) );
519+ }
520+
491521 public void testVariantsCarMapperNoSourceClass () {
492522 myFixture .configureByFile ( "CarMapperNoSourceClass.java" );
493523 complete ();
Original file line number Diff line number Diff line change 1+ /*
2+ * Copyright MapStruct Authors.
3+ *
4+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+ */
6+ package org .mapstruct .ap .test .complex ;
7+
8+ import org .mapstruct .Mapper ;
9+ import org .mapstruct .Mapping ;
10+
11+ @ Mapper
12+ public interface GenericCarWrapperMapper {
13+
14+ @ Mapping (target = "id" , source = "wrapper.car.<caret>winCode" )
15+ CarEntity toCarDto (CarWrapper <Car > wrapper );
16+ }
17+
18+ class CarEntity {
19+
20+ private String id ;
21+
22+ public String getId () {
23+ return id ;
24+ }
25+
26+ public void setId (String id ) {
27+ this .id = id ;
28+ }
29+
30+ }
31+
32+ class Car {
33+
34+ private String winCode ;
35+
36+ public String getWinCode () {
37+ return winCode ;
38+ }
39+
40+ public void setWinCode (String winCode ) {
41+ this .winCode = winCode ;
42+ }
43+ }
44+
45+ class CarWrapper <T > {
46+
47+ private T car ;
48+
49+ public T getCar () {
50+ return car ;
51+ }
52+
53+ public void setCar (T car ) {
54+ this .car = car ;
55+ }
56+ }
Original file line number Diff line number Diff line change 1+ /*
2+ * Copyright MapStruct Authors.
3+ *
4+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+ */
6+ package org .mapstruct .ap .test .complex ;
7+
8+ import org .mapstruct .Mapper ;
9+ import org .mapstruct .Mapping ;
10+ import org .mapstruct .MappingTarget ;
11+
12+ @ Mapper
13+ public interface GenericCarWrapperTargetMapper {
14+
15+ @ Mapping (source = "id" , target = "wrapper.car.<caret>winCode" )
16+ void update (@ MappingTarget CarWrapper <Car > wrapper , CarEntity entity );
17+ }
18+
19+ class CarEntity {
20+
21+ private String id ;
22+
23+ public String getId () {
24+ return id ;
25+ }
26+
27+ public void setId (String id ) {
28+ this .id = id ;
29+ }
30+ }
31+
32+ class Car {
33+
34+ private String winCode ;
35+
36+ public String getWinCode () {
37+ return winCode ;
38+ }
39+
40+ public void setWinCode (String winCode ) {
41+ this .winCode = winCode ;
42+ }
43+ }
44+
45+ class CarWrapper <T > {
46+
47+ private T car ;
48+
49+ public T getCar () {
50+ return car ;
51+ }
52+
53+ public void setCar (T car ) {
54+ this .car = car ;
55+ }
56+ }
You can’t perform that action at this time.
0 commit comments