From 594cda5c61965f2933ee7b2e167b7bafdd5b4ad8 Mon Sep 17 00:00:00 2001 From: Siddharth Srinivasan Date: Thu, 21 Aug 2025 17:42:38 +0530 Subject: [PATCH] Updated the JavaLangFeatures feature name extractor to work with JDK25+ EA Updated the code to obtain the parent diagnostics keys for feature-related errors/warnings to be agnostic to movement across different diagnostic types in different JDKs. For example, Warning -> LinterWarning --- .../server/telemetry/JavaLangFeatures.java | 110 ++++++++++++++---- .../JavaLanguageFeaturesEmitter.java | 4 +- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/JavaLangFeatures.java b/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/JavaLangFeatures.java index 7595b601..4f463dad 100644 --- a/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/JavaLangFeatures.java +++ b/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/JavaLangFeatures.java @@ -18,13 +18,20 @@ import com.sun.tools.javac.code.Source; import com.sun.tools.javac.resources.CompilerProperties; import com.sun.tools.javac.util.JCDiagnostic; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; class JavaLangFeatures { + private static final Logger LOG = Logger.getLogger(JavaLangFeatures.class.getName()); public static boolean isDiagnosticForUnsupportedFeatures(String diagnosticCode) { return Singleton.javacParentDiagnosticKeys.contains(diagnosticCode); @@ -47,45 +54,98 @@ private static class Singleton { static { Map featureFragments = new HashMap<>(); Set parentDiagnosticKeys = new HashSet<>(); - String prefix = "compiler.misc."; + String prefix = null; try { - final JCDiagnostic.Fragment fragment = CompilerProperties.Fragments.FeatureNotSupportedInSource((JCDiagnostic) null, null, null); - final String fragmentKey = fragment.key(); - final String fragmentCode = fragment.getCode(); - if (fragmentKey.startsWith(fragmentCode)) { - prefix = fragmentKey.substring(fragmentCode.length()); - } - - parentDiagnosticKeys.add(fragmentKey); - parentDiagnosticKeys.add(CompilerProperties.Fragments.FeatureNotSupportedInSourcePlural((JCDiagnostic) null, null, null).key()); - parentDiagnosticKeys.add(CompilerProperties.Errors.FeatureNotSupportedInSource((JCDiagnostic) null, null, null).key()); - parentDiagnosticKeys.add(CompilerProperties.Errors.FeatureNotSupportedInSourcePlural((JCDiagnostic) null, null, null).key()); - - parentDiagnosticKeys.add(CompilerProperties.Errors.PreviewFeatureDisabled((JCDiagnostic) null).key()); - parentDiagnosticKeys.add(CompilerProperties.Errors.PreviewFeatureDisabledPlural((JCDiagnostic) null).key()); - parentDiagnosticKeys.add(CompilerProperties.Warnings.PreviewFeatureUse((JCDiagnostic) null).key()); - parentDiagnosticKeys.add(CompilerProperties.Warnings.PreviewFeatureUsePlural((JCDiagnostic) null).key()); - - parentDiagnosticKeys.add(CompilerProperties.Errors.IsPreview(null).key()); - parentDiagnosticKeys.add(CompilerProperties.Warnings.IsPreview(null).key()); - parentDiagnosticKeys.add(CompilerProperties.Warnings.IsPreviewReflective(null).key()); - for (Source.Feature f : Source.Feature.values()) { try { - featureFragments.put(f.nameFragment().getCode(), f); + JCDiagnostic.Fragment nameFragment = f.nameFragment(); + featureFragments.put(nameFragment.getCode(), f); + + if (prefix == null) { + final String fragmentKey = nameFragment.key(); + final String fragmentCode = nameFragment.getCode(); + if (fragmentCode.length() > 0 && fragmentKey.endsWith(fragmentCode)) { + prefix = fragmentKey.substring(0, fragmentKey.length() - fragmentCode.length()); + } + } } catch (AssertionError | NullPointerException e) { // In case no error message code has been registered; for example: LOCAL_VARIABLE_TYPE_INFERENCE featureFragments.put(f.name(), f); } } + + Set diagnosticMethodNames = new HashSet<>(Arrays.asList( + "FeatureNotSupportedInSource", + "FeatureNotSupportedInSourcePlural", + "PreviewFeatureDisabled", + "PreviewFeatureDisabledPlural", + "PreviewFeatureUse", + "PreviewFeatureUsePlural", + "IsPreview", + "IsPreviewReflective" + )); + supplyKeyForEachDiagnosticName(diagnosticMethodNames, parentDiagnosticKeys::add); } catch (VirtualMachineError e) { throw e; - } catch (Throwable ignore) { + } catch (Throwable e) { + try { + LOG.log(Level.CONFIG, "Unexpected error initialising Java Language features and parent diagnostic codes: {0}", (Object) e); + } catch (Throwable ignore) { + } } - javacFragmentCodePrefix = prefix; + javacFragmentCodePrefix = prefix == null ? "compiler.misc." : prefix; javacParentDiagnosticKeys = parentDiagnosticKeys.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(parentDiagnosticKeys); fragmentCodeToFeature = featureFragments.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(featureFragments); } + + private static void supplyKeyForEachDiagnosticName(Set methodNames, Consumer action) { + if (methodNames == null || methodNames.isEmpty()) return; + final Object[][] emptyArgs = new Object[6][]; + for (int i = 0; i < emptyArgs.length; i++) { + emptyArgs[i] = new Object[i]; + } + int numInitErrors = 0; + Class[] classes = CompilerProperties.class.getClasses(); + for (Class nestedClass : classes) { + Method[] methods = Modifier.isStatic(nestedClass.getModifiers()) ? nestedClass.getMethods() : null; + if (methods == null || methods.length == 0) { + continue; + } + for (Method m : methods) { + try { + if (Modifier.isStatic(m.getModifiers()) + && JCDiagnostic.DiagnosticInfo.class.isAssignableFrom(m.getReturnType()) + && methodNames.contains(m.getName())) { + int numParams = m.getParameterCount(); + JCDiagnostic.DiagnosticInfo diag = (JCDiagnostic.DiagnosticInfo) m.invoke(null, numParams < emptyArgs.length ? emptyArgs[numParams] : Arrays.copyOf(emptyArgs[0], numParams)); + if (diag != null) { + action.accept(diag.key()); + } + } + } catch (VirtualMachineError e) { + throw e; + } catch (Throwable e) { + numInitErrors++; + try { + String name = null; + try { + name = m.getName(); + } catch (Throwable ignore) { + } + LOG.log(Level.FINE, "Unexpected error initialising diagnostic key for method \"{1}\", related to Java Language features: {0}", + name == null ? new Object[]{e, m} : new Object[]{e, name}); + } catch (Throwable ignore) { + } + } + } + } + if (numInitErrors > 0 && !LOG.isLoggable(Level.FINE)) { + try { + LOG.log(Level.CONFIG, "Unexpected {0} error(s) initialising diagnostic keys for methods related to Java Language features.", numInitErrors); + } catch (Throwable ignore) { + } + } + } } } diff --git a/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/JavaLanguageFeaturesEmitter.java b/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/JavaLanguageFeaturesEmitter.java index dfc70224..fb104a55 100644 --- a/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/JavaLanguageFeaturesEmitter.java +++ b/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/JavaLanguageFeaturesEmitter.java @@ -74,9 +74,9 @@ Set checkJavaFeatures() { task = (JavacTask) ToolProvider.getSystemJavaCompiler().getTask(null, null, dl, List.of("--source", "8", "--source-path", sourceInfo.getSourcesPath()), null, List.of(sourceInfo.source)); task.analyze(); } catch (IOException e) { - LOG.log(Level.FINE, "IO error while scanning Java Language features: {0}", e); + LOG.log(Level.FINE, "IO error while scanning Java Language features: {0}", (Object) e); } catch (IllegalArgumentException e) { - LOG.log(Level.CONFIG, "Invalid parsing parameters for scanning Java Language features: {0}", e); + LOG.log(Level.CONFIG, "Invalid parsing parameters for scanning Java Language features: {0}", (Object) e); } catch (RuntimeException ignored) { } return featuresUsed;