LightElement/LightMethodBuilder returning null from getContainingFile causing IllegalArgumentException in InjectedLanguageManager


I'm trying to write a plugin that provides support for a DSL that ends up generating java source, but without doing the generation through LightElement(s) like LightPsiClassBuilder and LightMethodBuilder. Currently it seems to be causing exceptions which might be messing with the type resolution of the types used in the light classes and methods. I looked into the intellij code and it appears that LightElement.getContainingFile only returns null, and that causes InjectedLanguageManagerImpl.isInjectedFragment to throw an IllegalArgumentException exception.


How should I deal with this? Always provide my own LightMethod based class rather than using LightMethodBuilder? Doesn't that make LightMethodBuilder useless?


Also, how should this kind of language support be done ideally? I have a bunch of DSL that declare interfaces and want to provide simulated declarations for them if the classes haven't been previously generated by an external build system.




Stacktrace is below.


```2019-08-23 08:03:33,996 [ 70949] ERROR - aemon.impl.PassExecutorService - Argument for @NotNull parameter 'file' of com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.isInjectedFragment must not be null
java.lang.IllegalArgumentException: Argument for @NotNull parameter 'file' of com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.isInjectedFragment must not be null
at com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl.$$$reportNull$$$0(
at com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl.isInjectedFragment(
at com.intellij.codeInspection.dataFlow.HardcodedContracts.getHardcodedContracts(
at com.intellij.codeInsight.DefaultInferredAnnotationProvider.getHardcodedContractAnnotation(
at com.intellij.codeInsight.DefaultInferredAnnotationProvider.findInferredAnnotation(
at com.intellij.codeInsight.InferredAnnotationsManagerImpl.findInferredAnnotation(
at com.intellij.codeInsight.AnnotationUtil.lambda$null$0(
at com.intellij.util.containers.ConcurrentFactoryMap$2.create(
at com.intellij.util.containers.ConcurrentFactoryMap.get(
at com.intellij.codeInsight.AnnotationUtil.findNonCodeAnnotation(
at com.intellij.codeInsight.AnnotationUtil.findAnnotation(
at com.intellij.codeInsight.AnnotationUtil.findAnnotation(
at com.intellij.codeInsight.AnnotationUtil.findAnnotation(
at com.intellij.codeInsight.AnnotationUtil.findAnnotationInHierarchy(
at com.intellij.codeInspection.dataFlow.JavaMethodContractUtil.findContractAnnotation(
at com.intellij.codeInspection.dataFlow.JavaMethodContractUtil.lambda$getContractInfo$1(
at com.intellij.psi.util.CachedValuesManager.lambda$getCachedValue$0(
at com.intellij.psi.impl.PsiCachedValueImpl.doCompute(
at com.intellij.util.CachedValueBase.getValueWithLock(
at com.intellij.psi.impl.PsiCachedValue.getValueWithLock(
at com.intellij.psi.impl.PsiCachedValueImpl.getValue(
at com.intellij.util.CachedValuesManagerImpl.getCachedValue(
at com.intellij.psi.util.CachedValuesManager.getCachedValue(
at com.intellij.psi.util.CachedValuesManager.getCachedValue(
at com.intellij.codeInspection.dataFlow.JavaMethodContractUtil.getContractInfo(
at com.intellij.codeInspection.dataFlow.JavaMethodContractUtil.isPure(
at com.siyeh.ig.bugs.IgnoreResultOfCallInspection$IgnoreResultOfCallVisitor.isPureMethod(
at com.siyeh.ig.bugs.IgnoreResultOfCallInspection$IgnoreResultOfCallVisitor.visitCalledExpression(
at com.siyeh.ig.bugs.IgnoreResultOfCallInspection$IgnoreResultOfCallVisitor.visitMethodCallExpression(
at com.intellij.codeInspection.InspectionEngine.acceptElements(
at com.intellij.codeInspection.InspectionEngine.createVisitorAndAcceptElements(
at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.runToolOnElements(
at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.lambda$null$5(
at com.intellij.util.AstLoadingFilter.disallowTreeLoading(
at com.intellij.util.AstLoadingFilter.disallowTreeLoading(
at com.intellij.codeInsight.daemon.impl.LocalInspectionsPass.lambda$visitPriorityElementsAndInit$6(
at com.intellij.concurrency.ApplierCompleter.execAndForkSubTasks(
at com.intellij.concurrency.ApplierCompleter.tryToExecAllList(
at com.intellij.concurrency.ApplierCompleter.execAndForkSubTasks(
at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(
at com.intellij.concurrency.ApplierCompleter.lambda$wrapInReadActionAndIndicator$1(
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(
at com.intellij.concurrency.ApplierCompleter.wrapInReadActionAndIndicator(
at com.intellij.concurrency.ApplierCompleter.lambda$compute$0(
at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(
at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(
at com.intellij.concurrency.ApplierCompleter.compute(
at java.util.concurrent.CountedCompleter.exec(
at java.util.concurrent.ForkJoinTask.doExec(
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(
at java.util.concurrent.ForkJoinPool.runWorker(

Comment actions Permalink

I'm not sure I understand why you try to achieve with Light* stuff, but maybe com.intellij.psi.augment.PsiAugmentProvider will be easier for DSL "fake"?

Comment actions Permalink

In previous experiments it appeared that a PsiAugmentProvider didn't do what I wanted, but perhaps it will. I'll look at it in a bit.


My current code uses a PsiElementFinder and ShortNamesCache to provide the ide with the fakes.


How should I be providing synthetic or fake elements to the IDE? Is the Light* api not the right thing to use? My code has been loosely inspired by the Android and Kotlin plugins.

Comment actions Permalink

So you're providing a complete "fake" class filled with fake methods? Or what exactly is your DSL about?

It seems the best solution seems to implement your own light class, see as inspiration

Comment actions Permalink

The DSL specifies interfaces with methods and properties. And the code expects certain generated APIs to exist, like a default implementation and some extra static methods on the interface. So I do end up needing to provide complete fake classes with methods.


I do have my own Light classes at the moment. Originally based on AndroidLightClassBase, but I've since switched to subclassing LightPsiClassBuilder and LightMethodBuilder instead.


Which is the correct plugin api to be using for this? PsiAugmentProvider or PsiElementFinder? Or both? I'd like to allow the IDE to pick up actual generated files after the external build generates them, but prior to that I need to provide fakes.

Comment actions Permalink

IMHO using custom light classes via PsiElementFinder  would be best choice here.

Comment actions Permalink

I ended up significantly modifying the parser and language definition to integrate a lot tighter into the java subsystem. It was a bit of a pain in the rear, but in the end it works rather well.

I'm now having an issue with another part of my plugin using PsiElementFinder to provide light classes. The ide is requesting and getting my custom classes, but then marks them red with the hover text "Cannot resolve symbol 'x'".

Basically theres a (generated) class that is only accessed statically, has a few inner (generated) classes, and is often accessed not using the full qualified name from the same package. Normally this should work fine, but for some reason the intellij highlighting decides it can't find the class. While debugging its like the path it takes through multiResolveImpl/OurGenericsResolver never bothers to ask plugins or look up symbols in the current package. It only goes as far as the current file and stops. Though a following step creating the quick fix for the "error" does query my ShortNamesCache and finds the class just fine.

It's really strange to me. Not sure exactly what I should be doing to get this to work properly.

TL;DR; Need to provide a public static final class that is visible to imports and in files in the same package without errors. Current attempt using PsiElementFinder and ShortNamesCache isn't doing the business.


Please sign in to leave a comment.