From ee9e51e70f7d48fa7917e46178f6595d75095871 Mon Sep 17 00:00:00 2001 From: Dusan Balek Date: Mon, 31 Jul 2023 15:26:40 +0200 Subject: [PATCH] Do not mark test methods as unused. --- .../modules/java/hints/bugs/Unused.java | 24 +++++++- java/junit.ui/nbproject/project.xml | 8 +++ .../junit/ui/actions/TestClassInfoTask.java | 29 +++++++++- java/spi.java.hints/apichanges.xml | 17 ++++++ .../nbproject/project.properties | 2 +- java/spi.java.hints/nbproject/project.xml | 3 +- .../spi/java/hints/unused/UsedDetector.java | 57 +++++++++++++++++++ 7 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 java/spi.java.hints/src/org/netbeans/spi/java/hints/unused/UsedDetector.java diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java index 34744e9678d9..ffef805bc831 100644 --- a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java +++ b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java @@ -20,9 +20,10 @@ import com.sun.source.tree.Tree.Kind; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; import javax.lang.model.element.ElementKind; +import org.netbeans.api.java.source.CompilationInfo; import org.netbeans.modules.java.editor.base.semantic.UnusedDetector; import org.netbeans.modules.java.editor.base.semantic.UnusedDetector.UnusedDescription; import org.netbeans.spi.editor.hints.ErrorDescription; @@ -33,6 +34,8 @@ import org.netbeans.spi.java.hints.HintContext; import org.netbeans.spi.java.hints.JavaFixUtilities; import org.netbeans.spi.java.hints.TriggerTreeKind; +import org.netbeans.spi.java.hints.unused.UsedDetector; +import org.openide.util.Lookup; import org.openide.util.NbBundle.Messages; /** @@ -56,12 +59,18 @@ public class Unused { @TriggerTreeKind(Kind.COMPILATION_UNIT) public static List unused(HintContext ctx) { List unused = UnusedDetector.findUnused(ctx.getInfo(), () -> ctx.isCanceled()); + List usedDetectors = unused.isEmpty() ? Collections.emptyList() : collectUsedDetectors(ctx.getInfo()); List result = new ArrayList<>(unused.size()); boolean detectUnusedPackagePrivate = ctx.getPreferences().getBoolean(DETECT_UNUSED_PACKAGE_PRIVATE, DETECT_UNUSED_PACKAGE_PRIVATE_DEFAULT); - for (UnusedDescription ud : unused) { + OUTER: for (UnusedDescription ud : unused) { if (ctx.isCanceled()) { break; } + for (UsedDetector detector : usedDetectors) { + if (detector.isUsed(ud.unusedElement, ud.unusedElementPath)) { + continue OUTER; + } + } if (!detectUnusedPackagePrivate && ud.packagePrivate) { continue; } @@ -73,6 +82,17 @@ public static List unused(HintContext ctx) { return result; } + private static List collectUsedDetectors(CompilationInfo info) { + List detectors = new ArrayList<>(); + for (UsedDetector.Factory factory : Lookup.getDefault().lookupAll(UsedDetector.Factory.class)) { + UsedDetector detector = factory.create(info); + if (detector != null) { + detectors.add(detector); + } + } + return detectors; + } + @Messages({ "# {0} - variable name", "ERR_NeitherReadOrWritten=Variable {0} is neither read or written to", diff --git a/java/junit.ui/nbproject/project.xml b/java/junit.ui/nbproject/project.xml index aa582301ea35..c3fc6fe5372e 100644 --- a/java/junit.ui/nbproject/project.xml +++ b/java/junit.ui/nbproject/project.xml @@ -207,6 +207,14 @@ 1.78 + + org.netbeans.spi.java.hints + + + + 1.56 + + org.openide.awt diff --git a/java/junit.ui/src/org/netbeans/modules/junit/ui/actions/TestClassInfoTask.java b/java/junit.ui/src/org/netbeans/modules/junit/ui/actions/TestClassInfoTask.java index cf6ca19c420e..051645ab7345 100644 --- a/java/junit.ui/src/org/netbeans/modules/junit/ui/actions/TestClassInfoTask.java +++ b/java/junit.ui/src/org/netbeans/modules/junit/ui/actions/TestClassInfoTask.java @@ -25,7 +25,6 @@ import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; -import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; @@ -39,6 +38,7 @@ import java.util.stream.Collectors; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.swing.text.BadLocationException; @@ -56,8 +56,8 @@ import org.netbeans.modules.java.testrunner.ui.spi.ComputeTestMethods; import org.netbeans.modules.java.testrunner.ui.spi.ComputeTestMethods.Factory; import org.netbeans.modules.parsing.spi.Parser; +import org.netbeans.spi.java.hints.unused.UsedDetector; import org.netbeans.spi.project.SingleMethod; -import org.openide.ErrorManager; import org.openide.filesystems.FileObject; import org.openide.util.Exceptions; import org.openide.util.lookup.ServiceProvider; @@ -255,6 +255,31 @@ public List computeTestMethods(CompilationInfo info) { } } + @ServiceProvider(service=UsedDetector.Factory.class) + public static final class UsedDetectorImpl implements UsedDetector.Factory { + + @Override + public UsedDetector create(CompilationInfo info) { + if (isTestSource(info.getFileObject())) { + List testMethods = TestClassInfoTask.doComputeTestMethods(info, new AtomicBoolean(), -1); + SourcePositions sp = info.getTrees().getSourcePositions(); + return (el, path) -> { + if (el.getKind() == ElementKind.METHOD) { + for (TestMethod tm : testMethods) { + if (tm.method().getMethodName().contentEquals(el.getSimpleName()) + && tm.start().getOffset() == sp.getStartPosition(path.getCompilationUnit(), path.getLeaf()) + && tm.end().getOffset() == sp.getEndPosition(path.getCompilationUnit(), path.getLeaf())) { + return true; + } + } + } + return false; + }; + } + return null; + } + } + @MimeRegistration(mimeType="text/x-java", service=org.netbeans.modules.gsf.testrunner.ui.spi.ComputeTestMethods.class) public static final class GenericComputeTestMethodsImpl implements org.netbeans.modules.gsf.testrunner.ui.spi.ComputeTestMethods { diff --git a/java/spi.java.hints/apichanges.xml b/java/spi.java.hints/apichanges.xml index d75974e8a5b2..54c6cb52e2d2 100644 --- a/java/spi.java.hints/apichanges.xml +++ b/java/spi.java.hints/apichanges.xml @@ -25,6 +25,23 @@ Java Hints SPI + + + Added UsedDetector SPI to mark an arbitrary elements as used + + + + +

+ The SPI to mark an arbitrary element as used was added. + Allows to suppress the standard NetBeans unused element detection + and to prevent the "unused" hint being displayed on given elements. + Can be used by various framework libraries that sometimes honor + annotations (i.e. injections or bindings) even on private methods. +

+
+ +
Added utility methods and SPI to open UI for hints diff --git a/java/spi.java.hints/nbproject/project.properties b/java/spi.java.hints/nbproject/project.properties index e8819b174c67..e2e9fef7d259 100644 --- a/java/spi.java.hints/nbproject/project.properties +++ b/java/spi.java.hints/nbproject/project.properties @@ -17,7 +17,7 @@ is.autoload=true javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial -spec.version.base=1.55.0 +spec.version.base=1.56.0 requires.nb.javac=true javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml diff --git a/java/spi.java.hints/nbproject/project.xml b/java/spi.java.hints/nbproject/project.xml index d5a4f3fb8a72..ac8c0a9c8000 100644 --- a/java/spi.java.hints/nbproject/project.xml +++ b/java/spi.java.hints/nbproject/project.xml @@ -504,9 +504,8 @@ org.netbeans.spi.java.hints - org.netbeans.spi.java.hints.annotations - org.netbeans.spi.java.hints.matching org.netbeans.spi.java.hints.support + org.netbeans.spi.java.hints.unused diff --git a/java/spi.java.hints/src/org/netbeans/spi/java/hints/unused/UsedDetector.java b/java/spi.java.hints/src/org/netbeans/spi/java/hints/unused/UsedDetector.java new file mode 100644 index 000000000000..f0d74712b62e --- /dev/null +++ b/java/spi.java.hints/src/org/netbeans/spi/java/hints/unused/UsedDetector.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.spi.java.hints.unused; + +import com.sun.source.util.TreePath; +import javax.lang.model.element.Element; +import org.netbeans.api.java.source.CompilationInfo; + +/** + * SPI to mark an arbitrary {@link Element} as used. Allows to suppress the standard NetBeans + * unused element detection and to prevent the "unused" hint being displayed on given elements. + * Can be used by various framework libraries that sometimes honor annotations + * (i.e. injections or bindings) even on private methods. + * + * @since 1.56 + */ +public interface UsedDetector { + + /** + * Checks whether given element should be marked as "used". + * @param el element to check + * @param path path to the element to check + * @return true if the given element should be marked as "used" + * @since 1.56 + */ + boolean isUsed(Element el, TreePath path); + + /** + * Factory to create {@link UsedDetector} instances. + * @since 1.56 + */ + public interface Factory { + /** + * Creates {@link UsedDetector} instance for the given {@link CompilationInfo}. + * @param info + * @return {@link UsedDetector} instance or null. + * @since 1.56 + */ + UsedDetector create(CompilationInfo info); + } +}