How do I get the methods generated in the PsiAugmentProvider to handle generics correctly

Answered

I'm now using LightMethodBuilder to add methods to some classes so they compile in idea, just like lombok does.

But the difference is, I didn't find the right way to handle generic inference,

This scenario is:

The method added using LightMethodBuilder is getChain(String chainId, ChainTypeReference<T> chainTypeReference)

The method's return value is SerialChainPipelineBuilder<T>.

After installing the plugin, everything looks fine, as shown in the next image

But when I use this method, generics fail to infer correctly, as follows

I also noticed that the idea plugin console was reporting an error when typing the first parameter

2024-01-26 21:50:40,534 [ 199831] SEVERE - #c.i.c.d.i.PassExecutorService - Parameter: PsiParameter:chainId; siblings: [PsiParameter:chainId]; ancestors: DummyHolder
java.lang.IllegalStateException: Parameter: PsiParameter:chainId; siblings: [PsiParameter:chainId]; ancestors: DummyHolder
	at com.intellij.psi.impl.source.PsiParameterImpl.getDeclarationScope(PsiParameterImpl.java:244)
	at com.intellij.codeInspection.i18n.NlsInfo.fromAnnotationContext(NlsInfo.java:194)
	at com.intellij.codeInspection.i18n.NlsInfo.forExpression(NlsInfo.java:96)
	at com.intellij.codeInspection.i18n.TitleCapitalizationInspection$1.getCapitalization(TitleCapitalizationInspection.java:81)
	at com.intellij.codeInspection.i18n.TitleCapitalizationInspection$1.visitElement(TitleCapitalizationInspection.java:53)
	at com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl.accept(PsiLiteralExpressionImpl.java:173)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner$InspectionProblemHolder.visitElement(InspectionRunner.java:531)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.processContext(InspectionRunner.java:380)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.lambda$inspect$4(InspectionRunner.java:159)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.lambda$processInOrderAsync$10(InspectionRunner.java:334)
	at com.intellij.openapi.application.impl.RwLockHolder.tryRunReadAction(RwLockHolder.kt:310)
	at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:925)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.lambda$processInOrderAsync$11(InspectionRunner.java:334)
	at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(ReadMostlyRWLock.java:199)
	at com.intellij.openapi.application.impl.RwLockHolder.executeByImpatientReader(RwLockHolder.kt:478)
	at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(ApplicationImpl.java:182)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.lambda$processInOrderAsync$12(InspectionRunner.java:340)
	at com.intellij.util.AstLoadingFilter.forceAllowTreeLoading(AstLoadingFilter.java:158)
	at com.intellij.util.AstLoadingFilter.forceAllowTreeLoading(AstLoadingFilter.java:150)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.lambda$processInOrderAsync$13(InspectionRunner.java:332)
	at com.intellij.util.AstLoadingFilter.disallowTreeLoading(AstLoadingFilter.java:129)
	at com.intellij.util.AstLoadingFilter.disallowTreeLoading(AstLoadingFilter.java:118)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.lambda$processInOrderAsync$14(InspectionRunner.java:332)
	at com.intellij.concurrency.JobLauncherImpl$2MyProcessQueueTask.lambda$call$0(JobLauncherImpl.java:460)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$12(CoreProgressManager.java:649)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:724)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:680)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:648)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:78)
	at com.intellij.concurrency.JobLauncherImpl$2MyProcessQueueTask.call(JobLauncherImpl.java:446)
	at com.intellij.concurrency.JobLauncherImpl$2MyProcessQueueTask.call(JobLauncherImpl.java:431)
	at com.intellij.concurrency.JobLauncherImpl$1.compute(JobLauncherImpl.java:416)
	at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:754)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
	at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:686)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.lambda$inspect$5(InspectionRunner.java:185)
	at com.intellij.codeInspection.InspectionEngine.withSession(InspectionEngine.java:246)
	at com.intellij.codeInsight.daemon.impl.InspectionRunner.inspect(InspectionRunner.java:114)
	at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.collectInformationWithProgress(LocalInspectionsPass.java:142)
	at com.intellij.codeInsight.daemon.impl.ProgressableTextEditorHighlightingPass.doCollectInformation(ProgressableTextEditorHighlightingPass.java:80)
	at com.intellij.codeHighlighting.TextEditorHighlightingPass.collectInformation(TextEditorHighlightingPass.java:55)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$2(PassExecutorService.java:410)
	at com.intellij.platform.diagnostic.telemetry.helpers.TraceKt.runWithSpanIgnoreThrows(trace.kt:85)
	at com.intellij.platform.diagnostic.telemetry.helpers.TraceUtil.runWithSpanThrows(TraceUtil.java:34)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$3(PassExecutorService.java:405)
	at com.intellij.openapi.application.impl.RwLockHolder.tryRunReadAction(RwLockHolder.kt:310)
	at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:925)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$doRun$4(PassExecutorService.java:396)
	at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$12(CoreProgressManager.java:649)
	at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:724)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:680)
	at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:648)
	at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:78)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.doRun(PassExecutorService.java:395)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$run$0(PassExecutorService.java:370)
	at com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl.cacheFileTypesInside(FileTypeManagerImpl.java:789)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.lambda$run$1(PassExecutorService.java:369)
	at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(ReadMostlyRWLock.java:199)
	at com.intellij.openapi.application.impl.RwLockHolder.executeByImpatientReader(RwLockHolder.kt:478)
	at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(ApplicationImpl.java:182)
	at com.intellij.codeInsight.daemon.impl.PassExecutorService$ScheduledPass.run(PassExecutorService.java:367)
	at com.intellij.concurrency.JobLauncherImpl$VoidForkJoinTask$1.exec(JobLauncherImpl.java:188)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)

The core code I used to build LightMethodBuilder is

public PsiMethod createMethod(Project project, String builderName, PsiManager psiManager, GlobalSearchScope resolveScope, PsiClass psiClass) {
        PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
        // builderNameForFactory is the method name
        String builderNameForFactory = NameUtil.getNameForFactory(builderName);
        // Get return type
        PsiClass returnBuilderClass = JavaPsiFacade.getInstance(project).findClass(builderName, resolveScope);
        if (returnBuilderClass == null) {
            return null;
        }
        PsiClassType objectType = elementFactory.createTypeByFQClassName("java.lang.Object", resolveScope);
        // Create type parameter T
        PsiTypeParameter typeParameter = elementFactory.createTypeParameter("T", new PsiClassType[]{objectType});
        PsiClassType genericBuilderType = elementFactory.createType(returnBuilderClass, new PsiType[]{elementFactory.createType(typeParameter)});
        // Create the method using LightMethodBuilder
        LightMethodBuilder methodBuilder = new LightMethodBuilder(psiManager, JavaLanguage.INSTANCE, builderNameForFactory)
                .setMethodReturnType(genericBuilderType)
                .addModifier(PsiModifier.PUBLIC)
                .addModifier(PsiModifier.STATIC);
        // Add the String chainId parameter
        PsiType javaLangStringType = elementFactory.createTypeFromText("java.lang.String", null);
        PsiParameter chainIdParameter = elementFactory.createParameter("chainId", javaLangStringType);
        methodBuilder.addParameter(chainIdParameter);
        // Build the ChainTypeReference<T> type
        PsiClass chainTypeReferenceClass = Objects.requireNonNull(elementFactory.createTypeByFQClassName(ParamsClass.CHAIN_TYPE_REFERENCE.getClassName(), resolveScope).resolve());
        // Create a ChainTypeReference type that contains the generic T
        PsiClassType chainTypeReferenceTypeWithGeneric = elementFactory.createType(chainTypeReferenceClass, new PsiType[]{elementFactory.createType(typeParameter)});
        // Add generic parameters to the method
        PsiParameter chainTypeReferenceParameter = elementFactory.createParameter("chainTypeReference", chainTypeReferenceTypeWithGeneric);
        methodBuilder.addTypeParameter(typeParameter);
        methodBuilder.addParameter(chainTypeReferenceParameter);
        methodBuilder.setNavigationElement(psiClass);
        methodBuilder.setContainingClass(psiClass);
        return methodBuilder;
    }

What can I do to get my generics to infer correctly and solve the error problem

0
2 comments

Hi Ben,

I don't see what can be a problem in your code. Could you please try using LighTypeParameterBuilder like here?
https://github.com/JetBrains/intellij-community/blob/master/java/java-tests/testSrc/com/intellij/java/psi/MiscPsiTest.java#L398-L403

0

Hi Karol,

I tried to fix it after reading the code sample

Finally, I found that the problem was that I seemed to misuse PsiParameter to generate the two parameters required for function signatures, such as String chainId, ChainTypeReference<T> chainTypeReference

When I bind chainTypeReference to generic T with the following code

PsiClassType objectType = elementFactory.createTypeByFQClassName("java.lang.Object", resolveScope);
// Create type parameter T
PsiTypeParameter typeParameter = elementFactory.createTypeParameter("T", new PsiClassType[]{objectType});

PsiClassType chainTypeReferenceTypeWithGeneric = elementFactory.createType(chainTypeReferenceClass, new PsiType[]{elementFactory.createType(typeParameter)});

PsiParameter chainTypeReferenceParameter = elementFactory.createParameter("chainTypeReference", chainTypeReferenceTypeWithGeneric);
methodBuilder.addParameter(chainTypeReferenceParameter)

I type the parameters after the call and the error described earlier occurs, like 

java.lang.IllegalStateException: Parameter: PsiParameter:chainTypeReference; siblings: [PsiParameter:chainTypeReference]; ancestors: DummyHolder

But in the code you give, I find that generic parameters seem to be used like this

methodBuilder.addParameter("chainTypeReference", chainTypeReferenceTypeWithGeneric);

So I tried to modify my use of parameters and generics, and it worked

Although none of this is directly related to LighTypeParameterBuilder, the code example you gave me still inspired me

I have posted my improved code here in the hope that it will be helpful to anyone who has encountered similar problems

public PsiMethod createMethod(Project project, String builderName, PsiManager psiManager, GlobalSearchScope resolveScope, PsiClass psiClass) {
        PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
        // builderNameForFactory is the method name
        String builderNameForFactory = NameUtil.getNameForFactory(builderName);
        // Get return type
        PsiClass returnBuilderClass = JavaPsiFacade.getInstance(project).findClass(builderName, resolveScope);
        if (returnBuilderClass == null) {
            return null;
        }
        PsiClassType objectType = elementFactory.createTypeByFQClassName("java.lang.Object", resolveScope);
        // Create type parameter T
        PsiTypeParameter typeParameter = elementFactory.createTypeParameter("T", new PsiClassType[]{objectType});
        PsiClassType genericBuilderType = elementFactory.createType(returnBuilderClass, new PsiType[]{elementFactory.createType(typeParameter)});
        // Create the method using LightMethodBuilder
        LightMethodBuilder methodBuilder = new LightMethodBuilder(psiManager, JavaLanguage.INSTANCE, builderNameForFactory)
                .setMethodReturnType(genericBuilderType)
                .addModifier(PsiModifier.PUBLIC)
                .addModifier(PsiModifier.STATIC);
        // Add the String chainId parameter
        PsiType javaLangStringType = PsiType.getJavaLangString(PsiManager.getInstance(project), resolveScope);
        methodBuilder.addParameter("chainId", javaLangStringType);
        // Build the ChainTypeReference<T> type
        PsiClass chainTypeReferenceClass = Objects.requireNonNull(elementFactory.createTypeByFQClassName(ParamsClass.CHAIN_TYPE_REFERENCE.getClassName(), resolveScope).resolve());
        // Create a ChainTypeReference type that contains the generic T
        PsiClassType chainTypeReferenceTypeWithGeneric = elementFactory.createType(chainTypeReferenceClass, new PsiType[]{elementFactory.createType(typeParameter)});
        // Add generic parameters to the method
        methodBuilder.addTypeParameter(typeParameter);
        methodBuilder.addParameter("chainTypeReference", chainTypeReferenceTypeWithGeneric);
        methodBuilder.setNavigationElement(psiClass);
        methodBuilder.setContainingClass(psiClass);
        return methodBuilder;
 }

Thank you so much for your help, Karol.

1

Please sign in to leave a comment.