antlr4-intellij-adaptor icon indicating copy to clipboard operation
antlr4-intellij-adaptor copied to clipboard

NullPointerException: Cannot invoke "java.util.List.get(int)" because "this.tokenElementTypes" is null

Open TysonMN opened this issue 2 years ago • 3 comments

When building my plugin, there is a NullPointerException in the ANTLR adaptor. It seems benign because the build continues, is eventually successful, and the built plugin works correctly.

Steps to reproduce

  1. Clean gradle cache
  2. execute the gradle task buildPlugin (actually, it should suffice to execute the gradle task buildSearchableOptions)

Expected behavior

Build completes successfully and without any errors

Actual behavior

Build completes successfully but with one error due to the following NullPointerException:

> Task :myModule:buildSearchableOptions
CompileCommand: exclude com/intellij/openapi/vfs/impl/FilePartNodeRoot.trieDescend bool exclude = true
2023-04-05 14:00:17,534 [   4793] SEVERE - #c.i.u.u.EdtInvocationManager - null
java.lang.reflect.InvocationTargetException
        at java.desktop/java.awt.EventQueue.invokeAndWait(EventQueue.java:1379)
        at java.desktop/java.awt.EventQueue.invokeAndWait(EventQueue.java:1354)
        at com.intellij.util.ui.EdtInvocationManager$SwingEdtInvocationManager.invokeAndWait(EdtInvocationManager.java:103)
        at com.intellij.util.ui.EdtInvocationManager.invokeAndWaitIfNeeded(EdtInvocationManager.java:84)
        at com.intellij.ide.ui.search.TraverseUIStarter.startup(TraverseUIStarter.java:115)
        at com.intellij.ide.ui.search.TraverseUIStarter.main(TraverseUIStarter.java:93)
        at com.intellij.idea.ApplicationLoader.initApplicationImpl$lambda-3(ApplicationLoader.kt:174)
        at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
        at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)
        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)
Caused by: java.lang.NullPointerException: Cannot invoke "java.util.List.get(int)" because "this.tokenElementTypes" is null
        at org.antlr.intellij.adaptor.lexer.ANTLRLexerAdaptor.getTokenType(ANTLRLexerAdaptor.java:188)
        at org.antlr.intellij.adaptor.lexer.ANTLRLexerAdaptor.getTokenType(ANTLRLexerAdaptor.java:178)
        at com.intellij.openapi.editor.ex.util.ValidatingLexerWrapper.getTokenType(ValidatingLexerWrapper.java:50)
        at com.intellij.openapi.editor.ex.util.LexerEditorHighlighter.doSetText(LexerEditorHighlighter.java:441)
        at com.intellij.openapi.editor.ex.util.LexerEditorHighlighter.setText(LexerEditorHighlighter.java:418)
        at com.intellij.openapi.editor.impl.EditorImpl.setHighlighter(EditorImpl.java:1359)
        at com.intellij.application.options.colors.SimpleEditorPreview.updateView(SimpleEditorPreview.java:192)
        at com.intellij.application.options.colors.NewColorAndFontPanel$3.schemeChanged(NewColorAndFontPanel.java:105)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at com.intellij.util.EventDispatcher.dispatchVoidMethod(EventDispatcher.java:120)
        at com.intellij.util.EventDispatcher.lambda$createMulticaster$1(EventDispatcher.java:85)
        at jdk.proxy2/jdk.proxy2.$Proxy80.schemeChanged(Unknown Source)
        at com.intellij.application.options.colors.SchemesPanel.resetSchemesCombo(SchemesPanel.java:56)
        at com.intellij.application.options.colors.NewColorAndFontPanel.resetSchemesCombo(NewColorAndFontPanel.java:156)
        at com.intellij.application.options.colors.NewColorAndFontPanel.reset(NewColorAndFontPanel.java:144)
        at com.intellij.application.options.colors.ColorAndFontOptions$InnerSearchableConfigurable.createPanel(ColorAndFontOptions.java:1333)
        at com.intellij.application.options.colors.ColorAndFontOptions$InnerSearchableConfigurable.createComponent(ColorAndFontOptions.java:1363)
        at com.intellij.ide.ui.search.SearchUtil.processConfigurables(SearchUtil.java:75)
        at com.intellij.ide.ui.search.TraverseUIStarter.lambda$startup$0(TraverseUIStarter.java:120)
        at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:308)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:779)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:730)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:724)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
        at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:749)
        at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:909)
        at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:756)
        at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$5(IdeEventQueue.java:437)
        at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:774)
        at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$6(IdeEventQueue.java:436)
        at com.intellij.openapi.application.TransactionGuardImpl.performActivity(TransactionGuardImpl.java:105)
        at com.intellij.ide.IdeEventQueue.performActivity(IdeEventQueue.java:615)
        at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$7(IdeEventQueue.java:434)
        at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:813)
        at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:480)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:207)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
        at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:92)

Suggested fix

I think the fix is rather straight forward. Here is the code in question:

https://github.com/antlr/antlr4-intellij-adaptor/blob/e002d4d7cadb9f3a0a5db9202fed8533417b8cf5/src/main/java/org/antlr/intellij/adaptor/lexer/ANTLRLexerAdaptor.java#L56

https://github.com/antlr/antlr4-intellij-adaptor/blob/e002d4d7cadb9f3a0a5db9202fed8533417b8cf5/src/main/java/org/antlr/intellij/adaptor/lexer/ANTLRLexerAdaptor.java#L132

https://github.com/antlr/antlr4-intellij-adaptor/blob/e002d4d7cadb9f3a0a5db9202fed8533417b8cf5/src/main/java/org/antlr/intellij/adaptor/lexer/ANTLRLexerAdaptor.java#L181-L189

If tokenElementTypes is null (on line 188), then I think this method should return null. I had a similar problem in my code, and returning null in this case seems to have fixed the problem.

Other

For the record, this happens with the 0.1 release, which is currently the only release.

TysonMN avatar Apr 05 '23 21:04 TysonMN

This exception stopped happening when I upgraded the version of IntelliJ from 2022.3 to 2023.1. Specifically, in my build.gradle file, I changed

intellij {
    version = "2023.1"
    plugins = ["PsiViewer:231-SNAPSHOT", "com.intellij.java"]
}

to

intellij {
    version = "2022.3"
    plugins = ["PsiViewer:223-SNAPSHOT", "com.intellij.java"]
}

TysonMN avatar Apr 20 '23 23:04 TysonMN

Same problem on IC233

2023-10-26 20:41:10,558 [  42461] SEVERE - #c.i.o.a.i.ActionUpdater - IntelliJ IDEA 2023.3 EAP  Build #IC-233.10527.20
2023-10-26 20:41:10,558 [  42461] SEVERE - #c.i.o.a.i.ActionUpdater - JDK: 17.0.8.1; VM: OpenJDK 64-Bit Server VM; Vendor: JetBrains s.r.o.
2023-10-26 20:41:10,558 [  42461] SEVERE - #c.i.o.a.i.ActionUpdater - OS: Windows 11
2023-10-26 20:41:10,558 [  42461] SEVERE - #c.i.o.a.i.ActionUpdater - Last Action: ShowSettings

oovm avatar Oct 26 '23 12:10 oovm

Hi, and sorry for the late reply. I believe this error can happen when an ANTLRLexerAdaptor is instantiated automatically by the IDE before the plugin is finished loading. In your case, it's even possible that the buildSearchableOptions task doesn't load your plugin at all, otherwise your custom ParserDefinition would have called PSIElementTypeFactory.defineLanguageIElementTypes() and tokenElementTypes wouldn't be null.

We had a similar problem in the StringTemplate 4 plugin, and a contributor ended up using a lazy value to ensure the initialization is done even when the ParserDefinition is not instantiated.

However, I think the problem could also be fixed by creating your ANTLRLexerAdaptors in a single place, for example a static factory method:

class AntlrLexerFactory {
    static {
	PSIElementTypeFactory.defineLanguageIElementTypes(
		YourLanguage.INSTANCE,
		YourLexer.tokenNames,
		YourParser.ruleNames
	);
    }

    public static ANTLRLexerAdaptor createLexer(Lexer lexer) {
        return new ANTLRLexerAdaptor(YourLanguage.INSTANCE, lexer);
    }
}

This way, the static block will ensure a proper initialization before a lexer is created. Then you can remove this initialization from your ParserDefinition's constructor.

bjansen avatar Jan 08 '24 20:01 bjansen

However, I think the problem could also be fixed by creating your ANTLRLexerAdaptors in a single place, for example a static factory method: [...] This way, the static block will ensure a proper initialization before a lexer is created. Then you can remove this initialization from your ParserDefinition's constructor.

Yes, this is a good suggestion.

Closing with this as the recommended solution.

TysonMN avatar Oct 17 '24 14:10 TysonMN