From 5f9a61d02dc054e4359ef083258faab91818d00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Fri, 27 Dec 2024 20:34:06 +0100 Subject: [PATCH 1/2] Improve handling of OccurencesFinder implementations that violate non-null contract --- .../csl/editor/semantic/MarkOccurrencesHighlighter.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ide/csl.api/src/org/netbeans/modules/csl/editor/semantic/MarkOccurrencesHighlighter.java b/ide/csl.api/src/org/netbeans/modules/csl/editor/semantic/MarkOccurrencesHighlighter.java index a4da4578f18f..dd9854400374 100644 --- a/ide/csl.api/src/org/netbeans/modules/csl/editor/semantic/MarkOccurrencesHighlighter.java +++ b/ide/csl.api/src/org/netbeans/modules/csl/editor/semantic/MarkOccurrencesHighlighter.java @@ -146,6 +146,14 @@ List processImpl(ParserResult info, Document doc, int caretPosition Map highlights = finder.getOccurrences(); + // Many implementatios of the OccurencesFinder don't follow the contract, + // that getOccurrences must not return null. Instead of blowing up with + // a NullPointer exception, log that problem and continue execution. + if (highlights == null) { + LOG.log(Level.WARNING, "org.netbeans.modules.csl.api.OccurrencesFinder.getOccurrences() non-null contract violation by {0}", language.getMimeType()); + highlights = Map.of(); + } + return List.copyOf(highlights.keySet()); } From 294f4afca1fd55563d1d022b4fd3d36a02f58098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Fri, 27 Dec 2024 20:34:49 +0100 Subject: [PATCH 2/2] GroovyOccurrencesFinder#getOccurrences must not return null --- .../nbproject/project.properties | 2 +- .../api/parser/GroovyOccurrencesFinder.java | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/groovy/groovy.editor/nbproject/project.properties b/groovy/groovy.editor/nbproject/project.properties index c0dc7d0d23ae..fab102853c1c 100644 --- a/groovy/groovy.editor/nbproject/project.properties +++ b/groovy/groovy.editor/nbproject/project.properties @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. javac.compilerargs=-Xlint -Xlint:-serial -javac.source=1.8 +javac.release=17 javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml diff --git a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/parser/GroovyOccurrencesFinder.java b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/parser/GroovyOccurrencesFinder.java index 1a4c65274434..86c84a8ba506 100644 --- a/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/parser/GroovyOccurrencesFinder.java +++ b/groovy/groovy.editor/src/org/netbeans/modules/groovy/editor/api/parser/GroovyOccurrencesFinder.java @@ -18,6 +18,7 @@ */ package org.netbeans.modules.groovy.editor.api.parser; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; @@ -41,7 +42,7 @@ /** * The (call-)proctocol for OccurrencesFinder is always: * - * 1.) setCaretPosition() = + * 1.) setCaretPosition() = NUMBER * 2.) run() * 3.) getOccurrences() * @@ -52,7 +53,7 @@ public class GroovyOccurrencesFinder extends OccurrencesFinder occurrences; + private Map occurrences = Map.of(); private FileObject file; private static final Logger LOG = Logger.getLogger(GroovyOccurrencesFinder.class.getName()); @@ -61,6 +62,7 @@ public GroovyOccurrencesFinder() { } @Override + @SuppressWarnings("ReturnOfCollectionOrArrayField") // immutable collection public Map getOccurrences() { LOG.log(Level.FINEST, "getOccurrences()\n"); //NOI18N return occurrences; @@ -102,7 +104,7 @@ public void run(GroovyParserResult result, SchedulerEvent event) { // FIXME parsing API - null file if (currentFile != file) { // Ensure that we don't reuse results from a different file - occurrences = null; + occurrences = Map.of(); file = currentFile; } @@ -130,15 +132,15 @@ public void run(GroovyParserResult result, SchedulerEvent event) { return; } - Map highlights = new HashMap(100); + Map highlights = new HashMap<>(100); highlight(path, highlights, document, caretPosition); if (isCancelled()) { return; } - if (highlights.size() > 0) { - Map translated = new HashMap(2 * highlights.size()); + if (!highlights.isEmpty()) { + Map translated = new HashMap<>(2 * highlights.size()); for (Map.Entry entry : highlights.entrySet()) { OffsetRange range = LexUtilities.getLexerOffsets(result, entry.getKey()); if (range != OffsetRange.NONE) { @@ -146,9 +148,9 @@ public void run(GroovyParserResult result, SchedulerEvent event) { } } highlights = translated; - this.occurrences = highlights; + this.occurrences = Collections.unmodifiableMap(highlights); } else { - this.occurrences = null; + this.occurrences = Map.of(); } } @@ -171,9 +173,9 @@ private static void highlight(AstPath path, Map scopeVisitor.collect(); for (ASTNode astNode : scopeVisitor.getOccurrences()) { OffsetRange range; - if (astNode instanceof FakeASTNode) { + if (astNode instanceof FakeASTNode fakeASTNode) { String text = astNode.getText(); - ASTNode orig = ((FakeASTNode) astNode).getOriginalNode(); + ASTNode orig = fakeASTNode.getOriginalNode(); int line = orig.getLineNumber(); int column = orig.getColumnNumber(); if (line > 0 && column > 0) {