antlr / antlr4-intellij-adaptor

A library to support the use of ANTLR grammars in jetbrains IDE plugins for building custom languages.
BSD 2-Clause "Simplified" License
208 stars 38 forks source link

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

Open TysonMN opened 1 year ago

TysonMN commented 1 year ago

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 commented 1 year ago

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"]
}
oovm commented 11 months ago

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
bjansen commented 8 months ago

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.