Stub count doesn't match stubbed node length crash in Custom Plugin

Answered

I need help tracking down a bug in my custom language plugin that uses Stubs. I've been debugging for days, and I'm running out of ideas. I suspect I'm missing something fundamental but need help finding it.

My plugin crashes on some file when the IDE is searching for usages in all places (i.e., CTRL-B or sometimes even when merely clicking on a definition). The error states `java.lang.AssertionError: Stub count doesn't match stubbed node length,` and this happens even when I clear the cache or change the stub number.

To make things worse, this only happens when the plugin is run from an installed IntelliJ and not when I use `runIDE` from the debugger. An issue that is only seen sometimes hints that it’s thread related, but I don't understand what's causing the issue and haven't been able to get more information on it.

 

Has anyone seen this before?

Is there some clue to what's wrong in the stacktrace?

Any ideas on how to expose this error using the debugger?

 

Here is the error message I get:

java.lang.AssertionError: Stub count doesn't match stubbed node length
at com.intellij.psi.impl.source.FileTrees.lambda$reconcilePsi$3(FileTrees.java:175)
at com.intellij.psi.impl.DebugUtil.performPsiModification(DebugUtil.java:562)
at com.intellij.psi.impl.source.FileTrees.reconcilePsi(FileTrees.java:168)
at com.intellij.psi.impl.source.FileTrees.withAst(FileTrees.java:135)
at com.intellij.psi.impl.source.PsiFileImpl.loadTreeElement(PsiFileImpl.java:209)
at com.intellij.psi.impl.source.PsiFileImpl.calcTreeElement(PsiFileImpl.java:709)
at com.intellij.psi.impl.source.PsiFileImpl.getNode(PsiFileImpl.java:914)
at com.intellij.psi.impl.source.PsiFileImpl.getFirstChild(PsiFileImpl.java:720)
at com.intellij.psi.PsiWalkingState$PsiTreeGuide.getFirstChild(PsiWalkingState.java:49)
at com.intellij.psi.PsiWalkingState$PsiTreeGuide.getFirstChild(PsiWalkingState.java:31)
at com.intellij.util.WalkingState.next(WalkingState.java:73)
at com.intellij.util.WalkingState.walkChildren(WalkingState.java:61)
at com.intellij.util.WalkingState.elementStarted(WalkingState.java:52)
at com.intellij.psi.PsiWalkingState.elementStarted(PsiWalkingState.java:79)
at com.intellij.psi.PsiRecursiveElementWalkingVisitor.visitElement(PsiRecursiveElementWalkingVisitor.java:48)
at com.intellij.psi.util.PsiTreeUtil$4.visitElement(PsiTreeUtil.java:877)
at com.intellij.psi.PsiElementVisitor.visitFile(PsiElementVisitor.java:34)
at com.intellij.psi.PsiRecursiveElementWalkingVisitor.visitFile(PsiRecursiveElementWalkingVisitor.java:70)
at com.intellij.extapi.psi.PsiFileBase.accept(PsiFileBase.java:70)
at com.intellij.psi.util.PsiTreeUtil.processElements(PsiTreeUtil.java:872)
at com.intellij.psi.util.PsiTreeUtil.findChildrenOfAnyType(PsiTreeUtil.java:334)
at com.intellij.psi.util.PsiTreeUtil.findChildrenOfType(PsiTreeUtil.java:297)
at studio.edaphic.systemverilog.psi.ext.SVIncludeOwnerKt.getIncludes(SVIncludeOwner.kt:19)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processIncludeFiles(NameResolution.kt:575)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processLexicalDeclarations(NameResolution.kt:340)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processIncludeFiles(NameResolution.kt:588)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processLexicalDeclarations(NameResolution.kt:340)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.access$processLexicalDeclarations(NameResolution.kt:1)
at studio.edaphic.systemverilog.resolve.NameResolutionKt$processNestedScopesUpwards$2.invoke(NameResolution.kt:649)
at studio.edaphic.systemverilog.resolve.NameResolutionKt$processNestedScopesUpwards$2.invoke(NameResolution.kt)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.walkUp(NameResolution.kt:713)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processNestedScopesUpwards(NameResolution.kt:620)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processIncludeScopesUpwards(NameResolution.kt:692)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processIncludeScopesUpwards(NameResolution.kt:695)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processScopedPathResolveVariants(NameResolution.kt:156)
at studio.edaphic.systemverilog.resolve.ref.SVScopedPathReferenceImpl$resolveInner$1.invoke(SVScopedPathReferenceImpl.kt:24)
at studio.edaphic.systemverilog.resolve.ref.SVScopedPathReferenceImpl$resolveInner$1.invoke(SVScopedPathReferenceImpl.kt:16)
at studio.edaphic.systemverilog.resolve.ProcessorsKt.collectResolveVariants(Processors.kt:41)
at studio.edaphic.systemverilog.resolve.ref.SVScopedPathReferenceImpl.resolveInner(SVScopedPathReferenceImpl.kt:24)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase$Resolver.resolve(SVReferenceBase.kt:72)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase$Resolver.resolve(SVReferenceBase.kt:70)
at com.intellij.psi.impl.source.resolve.ResolveCache.lambda$resolve$0(ResolveCache.java:150)
at com.intellij.openapi.util.RecursionManager$2.doPreventingRecursion(RecursionManager.java:98)
at com.intellij.psi.impl.source.resolve.ResolveCache.resolve(ResolveCache.java:149)
at com.intellij.psi.impl.source.resolve.ResolveCache.resolveWithCaching(ResolveCache.java:239)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.advancedCachedMultiResolve(SVReferenceBase.kt:43)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.multiResolve(SVReferenceBase.kt:39)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.multiResolve(SVReferenceBase.kt:36)
at com.intellij.psi.PsiPolyVariantReferenceBase.resolve(PsiPolyVariantReferenceBase.java:47)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.resolve(SVReferenceBase.kt:30)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processLexicalDeclarations(NameResolution.kt:469)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.access$processLexicalDeclarations(NameResolution.kt:1)
at studio.edaphic.systemverilog.resolve.NameResolutionKt$processNestedScopesUpwards$2.invoke(NameResolution.kt:649)
at studio.edaphic.systemverilog.resolve.NameResolutionKt$processNestedScopesUpwards$2.invoke(NameResolution.kt)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.walkUp(NameResolution.kt:713)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processNestedScopesUpwards(NameResolution.kt:620)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processHierarchicalPathResolveVariants(NameResolution.kt:136)
at studio.edaphic.systemverilog.resolve.ref.SVHierarchicalPathReferenceImpl$resolveInner$1.invoke(SVHierarchicalPathReferenceImpl.kt:24)
at studio.edaphic.systemverilog.resolve.ref.SVHierarchicalPathReferenceImpl$resolveInner$1.invoke(SVHierarchicalPathReferenceImpl.kt:16)
at studio.edaphic.systemverilog.resolve.ProcessorsKt.collectResolveVariants(Processors.kt:41)
at studio.edaphic.systemverilog.resolve.ref.SVHierarchicalPathReferenceImpl.resolveInner(SVHierarchicalPathReferenceImpl.kt:24)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase$Resolver.resolve(SVReferenceBase.kt:72)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase$Resolver.resolve(SVReferenceBase.kt:70)
at com.intellij.psi.impl.source.resolve.ResolveCache.lambda$resolve$0(ResolveCache.java:150)
at com.intellij.openapi.util.RecursionManager$2.doPreventingRecursion(RecursionManager.java:98)
at com.intellij.psi.impl.source.resolve.ResolveCache.resolve(ResolveCache.java:149)
at com.intellij.psi.impl.source.resolve.ResolveCache.resolveWithCaching(ResolveCache.java:239)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.advancedCachedMultiResolve(SVReferenceBase.kt:43)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.multiResolve(SVReferenceBase.kt:39)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.multiResolve(SVReferenceBase.kt:36)
at com.intellij.psi.PsiPolyVariantReferenceBase.resolve(PsiPolyVariantReferenceBase.java:47)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.resolve(SVReferenceBase.kt:30)
at studio.edaphic.systemverilog.resolve.NameResolutionKt.processHierarchicalPathResolveVariants(NameResolution.kt:40)
at studio.edaphic.systemverilog.resolve.ref.SVHierarchicalPathReferenceImpl$resolveInner$1.invoke(SVHierarchicalPathReferenceImpl.kt:24)
at studio.edaphic.systemverilog.resolve.ref.SVHierarchicalPathReferenceImpl$resolveInner$1.invoke(SVHierarchicalPathReferenceImpl.kt:16)
at studio.edaphic.systemverilog.resolve.ProcessorsKt.collectResolveVariants(Processors.kt:41)
at studio.edaphic.systemverilog.resolve.ref.SVHierarchicalPathReferenceImpl.resolveInner(SVHierarchicalPathReferenceImpl.kt:24)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase$Resolver.resolve(SVReferenceBase.kt:72)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase$Resolver.resolve(SVReferenceBase.kt:70)
at com.intellij.psi.impl.source.resolve.ResolveCache.lambda$resolve$0(ResolveCache.java:150)
at com.intellij.openapi.util.RecursionManager$2.doPreventingRecursion(RecursionManager.java:98)
at com.intellij.psi.impl.source.resolve.ResolveCache.resolve(ResolveCache.java:149)
at com.intellij.psi.impl.source.resolve.ResolveCache.resolveWithCaching(ResolveCache.java:239)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.advancedCachedMultiResolve(SVReferenceBase.kt:43)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.multiResolve(SVReferenceBase.kt:39)
at studio.edaphic.systemverilog.resolve.ref.SVReferenceBase.multiResolve(SVReferenceBase.kt:36)
at com.intellij.psi.PsiPolyVariantReferenceBase.isReferenceTo(PsiPolyVariantReferenceBase.java:53)
at com.intellij.psi.search.SingleTargetRequestResultProcessor.processTextOccurrence(SingleTargetRequestResultProcessor.java:38)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$3.lambda$execute$0(PsiSearchHelperImpl.java:783)
at com.intellij.psi.impl.search.LowLevelSearchUtil.processTreeUp(LowLevelSearchUtil.java:138)
at com.intellij.psi.impl.search.LowLevelSearchUtil.processElementsAtOffsets(LowLevelSearchUtil.java:225)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$3.execute(PsiSearchHelperImpl.java:779)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$2.processInReadAction(PsiSearchHelperImpl.java:232)
at com.intellij.psi.impl.search.PsiSearchHelperImpl$2.processInReadAction(PsiSearchHelperImpl.java:223)
at com.intellij.openapi.application.ReadActionProcessor.lambda$process$0(ReadActionProcessor.java:28)
at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:973)
at com.intellij.openapi.application.ReadAction.compute(ReadAction.java:57)
at com.intellij.openapi.application.ReadActionProcessor.process(ReadActionProcessor.java:28)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.lambda$processVirtualFile$6(PsiSearchHelperImpl.java:426)
at com.intellij.openapi.application.impl.ApplicationImpl.tryRunReadAction(ApplicationImpl.java:1168)
at com.intellij.openapi.application.ex.ApplicationUtil.tryRunReadAction(ApplicationUtil.java:43)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.processVirtualFile(PsiSearchHelperImpl.java:410)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.lambda$processPsiFileRoots$2(PsiSearchHelperImpl.java:306)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.lambda$null$3(PsiSearchHelperImpl.java:363)
at com.intellij.openapi.application.impl.ReadMostlyRWLock.executeByImpatientReader(ReadMostlyRWLock.java:147)
at com.intellij.openapi.application.impl.ApplicationImpl.executeByImpatientReader(ApplicationImpl.java:222)
at com.intellij.psi.impl.search.PsiSearchHelperImpl.lambda$processFilesConcurrentlyDespiteWriteActions$4(PsiSearchHelperImpl.java:362)
at com.intellij.concurrency.ApplierCompleter.execAndForkSubTasks(ApplierCompleter.java:133)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:582)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:532)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:87)
at com.intellij.concurrency.ApplierCompleter.wrapInReadActionAndIndicator(ApplierCompleter.java:116)
at com.intellij.concurrency.ApplierCompleter.compute(ApplierCompleter.java:99)
at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
5 comments

Not sure if this is the case in your custom language but I had issues in my plugin with stubs and intermittent failures with building psi tree if the PsiElement and a contained LeafPsiElement had the same IElementType. 

Nothing stops you from creating a PsiElement which shares the IElementType with a LeafPsiElement but then stubs and PsiElement replacements will tend to mess up because both have the same IElementType.

0

It's hard to tell what's wrong by this description. Some causes we've seen include:

  • leaf/composite PSI mismatch (thanks Vladimir for pointing that out!)
  • LightStubBuilder not working in the same way as the normal one would (by building stubs by AST)
  • errors in incremental parsing (e.g. IReparseableElementType#isParsable incorrectly returning true on broken code, when full reparse would produce a different result)
  • asymmetric stub serialization/deserialization code
  • lexer/parser/AST/PSI/stubs depending on something more than the file's content: other file, project settings, etc

You can also run the "normal" IDEA with debug parameters (e.g. -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5239), connect to it, put a breakpoint and try to figure out the reason.

0

Thank you both. 

I need to support pre-compiler macro expansion, so I do insert zero length IElementTypes. I suspect this is what screws it up when my inserted tokens basically create a new class item (like a function or a task). I'm mostly surprised why I haven't seen this issue before (or at least as frequently).

I got a recommendation, early on (from CLion developer ) to extend my inserted tokens from `com.intellij.lang.ForeignLeafType`. I don't do this today (I insert zero length IElement tokens - which leads to all types of issues). So I would like switching to this. 

Are there any examples of how to use ForeignLeafType when inserting tokens? In the source, it doesn't seem used. I'd love to understand in some more detail how CLion handles this insertion

0

There is also com.intellij.psi.impl.source.tree.ForeignLeafPsiElement that comes together with com.intellij.lang.ForeignLeafType - basically, it's a more or less normal zero-length leaf element, but the platform is aware of it and adjusts its behavior when necessary. However, CLion doesn't use PSI stubs, so this use case might be less explored.

It's also used in the Fortran plugin (https://github.com/satamas/fortran-plugin/blob/master/src/main/java/org/jetbrains/fortran/lang/psi/FortranIncludeForeignLeafElement.kt, https://github.com/satamas/fortran-plugin/blob/master/src/main/kotlin/org/jetbrains/fortran/lang/lexer/FortranIncludeProcessingLexer.kt#L58), hopefully, it would be helpful for you.

0

Thank you, Dmitry,

I will try switching to `ForeignLeafType` as I believe (hope?) that this will solve my issue. I think the issue is that some defines insert class methods and with my current method (using IElementType directly) the method's name IDENTIFIER is blank which causes issues with stubbing. I believe that using `ForeignLeafType`will solve several issues I'm having - among them having access to the method name during stubbing. 

It's great to have the Fortran example to look at!

Thank you all!

/Robert

0

Please sign in to leave a comment.