From 5c3228f4f1440a47c93a36cbe24f0ba707211e9e Mon Sep 17 00:00:00 2001 From: Michael Bien Date: Thu, 29 Jan 2026 14:01:47 +0100 Subject: [PATCH] StaticImport hint should support records and other class kinds uses javac's Element.getKind().isDeclaredType() as filter which matches all class kinds (interfaces, records etc). minor code improvements and indentation fixes --- .../modules/java/hints/Bundle.properties | 4 +- .../modules/java/hints/StaticImport.java | 56 +++++++------------ .../modules/java/hints/StaticImportTest.java | 13 ++++- 3 files changed, 34 insertions(+), 39 deletions(-) diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties b/java/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties index 62288327ac1d..693c631f1b43 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties +++ b/java/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties @@ -283,7 +283,9 @@ HINT_SerialVersionUID_Generated=Add generated serialVersionUID FieldForUnusedParamCustomizer.finalFields.text=Fields are final ACSD_Final_Fields=Make fields created by this hint final. -DSC_StaticImport=Convert a static method/field/enum-field reference to use a static import. Feedback to http://www.netbeans.org/issues/show_bug.cgi?id=89258 +DSC_StaticImport=Convert a static method/field/enum-field reference to use a static import.\ +

Math.abs(-1) -> abs(-1)

+ DN_StaticImport=Static imports ERR_StaticImport=Convert to static import HINT_StaticImport=Convert {0} to static import diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/StaticImport.java b/java/java.hints/src/org/netbeans/modules/java/hints/StaticImport.java index 0628ceeaef46..5e2967337127 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/StaticImport.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/StaticImport.java @@ -28,7 +28,6 @@ import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; import com.sun.source.util.TreePath; -import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; @@ -42,7 +41,6 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; import org.netbeans.api.java.source.CompilationInfo; -import org.netbeans.api.java.source.ElementUtilities; import org.netbeans.api.java.source.GeneratorUtilities; import org.netbeans.api.java.source.TreeMaker; import org.netbeans.api.java.source.TreePathHandle; @@ -77,12 +75,13 @@ * * @author Sam Halliday * @author markiewb - * @see RFE 89258 - * @see Static Imports */ @Hint(category="rules15", displayName="#DN_StaticImport", description="#DSC_StaticImport", severity=Severity.HINT, enabled=false, suppressWarnings={"", "StaticImport"}, minSourceVersion = "5") public class StaticImport { + + private static final Set SUPPORTED_TYPES = EnumSet.of(ElementKind.METHOD, ElementKind.ENUM_CONSTANT, ElementKind.FIELD); @TriggerTreeKind(Kind.MEMBER_SELECT) public static List run(HintContext ctx) { @@ -90,23 +89,22 @@ public static List run(HintContext ctx) { TreePath treePath = ctx.getPath(); Element e = info.getTrees().getElement(treePath); - EnumSet supportedTypes = EnumSet.of(ElementKind.METHOD, ElementKind.ENUM_CONSTANT, ElementKind.FIELD); - if (e == null || !e.getModifiers().contains(Modifier.STATIC) || !supportedTypes.contains(e.getKind())) { + if (e == null || !e.getModifiers().contains(Modifier.STATIC) || !SUPPORTED_TYPES.contains(e.getKind())) { return null; } if (ElementKind.METHOD == e.getKind()) { TreePath mitp = treePath.getParentPath(); if (mitp == null || mitp.getLeaf().getKind() != Kind.METHOD_INVOCATION) { - return null; - } + return null; + } if (((MethodInvocationTree) mitp.getLeaf()).getMethodSelect() != treePath.getLeaf()) { - return null; - } + return null; + } List typeArgs = ((MethodInvocationTree) mitp.getLeaf()).getTypeArguments(); if (typeArgs != null && !typeArgs.isEmpty()) { - return null; - } + return null; + } } Element enclosingEl = e.getEnclosingElement(); if (enclosingEl == null) { @@ -122,7 +120,7 @@ public static List run(HintContext ctx) { return null; } Element klass = info.getTrees().getElement(cc); - if (klass == null || klass.getKind() != ElementKind.CLASS) { + if (klass == null || !klass.getKind().isDeclaredType()) { return null; } String fqn = null; @@ -143,7 +141,7 @@ public static List run(HintContext ctx) { if (ctx.isCanceled()) { return null; } - return Collections.singletonList(ed); + return List.of(ed); } public static final class FixImpl extends JavaFix { @@ -189,31 +187,18 @@ protected void performRewrite(TransformationContext ctx) throws Exception { return; } CompilationUnitTree cut = (CompilationUnitTree) copy.resolveRewriteTarget(copy.getCompilationUnit()); - CompilationUnitTree nue = GeneratorUtilities.get(copy).addImports(cut, Collections.singleton(e)); + CompilationUnitTree nue = GeneratorUtilities.get(copy).addImports(cut, Set.of(e)); copy.rewrite(cut, nue); } } - /** - * @param info - * @return true if the source level supports the static import language feature - */ - private static boolean supportsStaticImports(CompilationInfo info) { - return info.getSourceVersion().compareTo(SourceVersion.RELEASE_5) >= 0; - } - // returns true if a METHOD is enclosed in element with simple name sn private static boolean hasMethodWithSimpleName(CompilationInfo info, Element element, final String sn) { - Iterable members = - info.getElementUtilities().getMembers(element.asType(), new ElementUtilities.ElementAcceptor() { - - @Override - public boolean accept(Element e, TypeMirror type) { - return e.getKind() == ElementKind.METHOD && e.getSimpleName().toString().equals(sn); - } - }); - return members.iterator().hasNext(); + return info.getElementUtilities().getMembers( + element.asType(), + (elem, type) -> elem.getKind() == ElementKind.METHOD && elem.getSimpleName().toString().equals(sn) + ).iterator().hasNext(); } /** @@ -266,7 +251,7 @@ private static boolean hasStaticImportSimpleNameClash(CompilationInfo info, Stri */ private static boolean isSubTypeOrInnerOfSubType(CompilationInfo info, Element t1, Element t2) { boolean isSubtype = info.getTypes().isSubtype(t1.asType(), t2.asType()); - boolean isInnerClass = t1.getEnclosingElement().getKind() == ElementKind.CLASS; + boolean isInnerClass = t1.getEnclosingElement().getKind().isDeclaredType(); return isSubtype || (isInnerClass && info.getTypes().isSubtype(t1.getEnclosingElement().asType(), t2.asType())); } @@ -278,15 +263,12 @@ private static boolean isSubTypeOrInnerOfSubType(CompilationInfo info, Element t * methods in klass (which may be an inner or static class). */ private static boolean hasMethodNameClash(CompilationInfo info, Element klass, String simpleName) { - assert klass != null; - assert klass.getKind() == ElementKind.CLASS; - // check the members and inherited members of the klass if (hasMethodWithSimpleName(info, klass, simpleName)) { return true; } Element klassEnclosing = klass.getEnclosingElement(); - return (klassEnclosing != null && klassEnclosing.getKind() == ElementKind.CLASS && hasMethodWithSimpleName(info, klassEnclosing, simpleName)); + return klassEnclosing != null && klassEnclosing.getKind().isDeclaredType() && hasMethodWithSimpleName(info, klassEnclosing, simpleName); } /** diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/StaticImportTest.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/StaticImportTest.java index 07800c5a03ee..44af98fe3387 100644 --- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/StaticImportTest.java +++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/StaticImportTest.java @@ -99,6 +99,12 @@ public void testStaticImportHint1() throws Exception { String golden = "package test; import static java.lang.Math.abs; public class Test { public Test() { abs(1); } }"; performFixTest(test, golden); } + + public void testStaticImportHint1_InRecord() throws Exception { + String test = "package test; public record Test(int n) { public Test { Math.|abs(n); } }"; + String golden = "package test; import static java.lang.Math.abs; public record Test(int n) { public Test { abs(n); } }"; + performFixTest(test, golden, 17); + } public void testStaticImportHint2() throws Exception { String test = "package test; public class Test { public Test() { Test.get|Logger(); } public static void getLogger() { } }"; @@ -161,10 +167,14 @@ public void testIgnoreClass() throws Exception { performAnalysisTest(test); } + private void performFixTest(String test, String golden) throws Exception { + performFixTest(test, golden, 8); + } + // test is single line source code for test.Test, | in the member select, space before // golden is the output to test against // sn is the simple name of the static method - private void performFixTest(String test, String golden) throws Exception { + private void performFixTest(String test, String golden, int level) throws Exception { int offset = test.indexOf("|"); assertTrue(offset != -1); int end = test.indexOf("(", offset) - 1; @@ -172,6 +182,7 @@ private void performFixTest(String test, String golden) throws Exception { int start = test.lastIndexOf(" ", offset) + 1; assertTrue(start > 0); HintTest.create() + .sourceLevel(level) .input(test.replace("|", "")) .run(StaticImport.class) .findWarning("0:" + start + "-0:" + end + ":hint:" + NbBundle.getMessage(StaticImport.class, "ERR_StaticImport"))