From 6b8e152d36a9416928a5efcac23ece6ac2ec1976 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. --- java/java.editor.base/nbproject/project.xml | 8 +++ .../editor/base/semantic/UnusedDetector.java | 43 +++++++++++--- .../modules/java/hints/bugs/Unused.java | 1 - 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 +++++++++++++++++++ 9 files changed, 154 insertions(+), 14 deletions(-) create mode 100644 java/spi.java.hints/src/org/netbeans/spi/java/hints/unused/UsedDetector.java diff --git a/java/java.editor.base/nbproject/project.xml b/java/java.editor.base/nbproject/project.xml index d1ffc4827582..8f79fbf99362 100644 --- a/java/java.editor.base/nbproject/project.xml +++ b/java/java.editor.base/nbproject/project.xml @@ -122,6 +122,14 @@ 1.87 + + org.netbeans.spi.java.hints + + + + 1.56 + + org.openide.filesystems diff --git a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/UnusedDetector.java b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/UnusedDetector.java index 161855c43dab..e129842e4538 100644 --- a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/UnusedDetector.java +++ b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/UnusedDetector.java @@ -48,6 +48,8 @@ import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiFunction; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -69,8 +71,10 @@ import org.netbeans.api.project.ProjectUtils; import org.netbeans.api.project.SourceGroup; import org.netbeans.spi.java.classpath.support.ClassPathSupport; +import org.netbeans.spi.java.hints.unused.UsedDetector; import org.openide.filesystems.FileObject; import org.openide.util.Exceptions; +import org.openide.util.Lookup; /** * @@ -114,6 +118,18 @@ public static List findUnused(CompilationInfo info, Callable< UnusedVisitor uv = new UnusedVisitor(info); uv.scan(info.getCompilationUnit(), null); + AtomicReference> usedDetectors = new AtomicReference<>(); + BiFunction markedAsUsed = (el, path) -> { + if (usedDetectors.get() == null) { + usedDetectors.set(collectUsedDetectors(info)); + } + for (UsedDetector detector : usedDetectors.get()) { + if (detector.isUsed(el, path)) { + return true; + } + } + return false; + }; List result = new ArrayList<>(); for (Entry e : uv.element2Declaration.entrySet()) { Element el = e.getKey(); @@ -124,11 +140,11 @@ public static List findUnused(CompilationInfo info, Callable< if (isLocalVariableClosure(el)) { boolean isWritten = uses.contains(UseTypes.WRITTEN); boolean isRead = uses.contains(UseTypes.READ); - if (!isWritten && !isRead) { + if (!isWritten && !isRead && !markedAsUsed.apply(el, declaration)) { result.add(new UnusedDescription(el, declaration, isPkgPrivate, UnusedReason.NOT_WRITTEN_READ)); - } else if (!isWritten) { + } else if (!isWritten && !markedAsUsed.apply(el, declaration)) { result.add(new UnusedDescription(el, declaration, isPkgPrivate, UnusedReason.NOT_WRITTEN)); - } else if (!isRead) { + } else if (!isRead && !markedAsUsed.apply(el, declaration)) { result.add(new UnusedDescription(el, declaration, isPkgPrivate, UnusedReason.NOT_READ)); } } else if (el.getKind().isField() && (isPrivate || isPkgPrivate)) { @@ -136,13 +152,13 @@ public static List findUnused(CompilationInfo info, Callable< boolean isWritten = uses.contains(UseTypes.WRITTEN); boolean isRead = uses.contains(UseTypes.READ); if (!isWritten && !isRead) { - if (isPrivate || isUnusedInPkg(info, el, cancel)) { + if ((isPrivate || isUnusedInPkg(info, el, cancel)) && !markedAsUsed.apply(el, declaration)) { result.add(new UnusedDescription(el, declaration, isPkgPrivate, UnusedReason.NOT_WRITTEN_READ)); } - } else if (!isWritten) { + } else if (!isWritten && !markedAsUsed.apply(el, declaration)) { result.add(new UnusedDescription(el, declaration, isPkgPrivate, UnusedReason.NOT_WRITTEN)); } else if (!isRead) { - if (isPrivate || isUnusedInPkg(info, el, cancel)) { + if ((isPrivate || isUnusedInPkg(info, el, cancel)) && !markedAsUsed.apply(el, declaration)) { result.add(new UnusedDescription(el, declaration, isPkgPrivate, UnusedReason.NOT_READ)); } } @@ -152,13 +168,13 @@ public static List findUnused(CompilationInfo info, Callable< if (!isSerializationMethod(info, method) && !uses.contains(UseTypes.USED) && !info.getElementUtilities().overridesMethod(method) && !lookedUpElement(el, uv.type2LookedUpMethods, uv.allStringLiterals) && !SourceUtils.isMainMethod(method)) { - if (isPrivate || isUnusedInPkg(info, el, cancel)) { + if ((isPrivate || isUnusedInPkg(info, el, cancel)) && !markedAsUsed.apply(el, declaration)) { result.add(new UnusedDescription(el, declaration, isPkgPrivate, UnusedReason.NOT_USED)); } } } else if ((el.getKind().isClass() || el.getKind().isInterface()) && (isPrivate || isPkgPrivate)) { if (!uses.contains(UseTypes.USED)) { - if (isPrivate || isUnusedInPkg(info, el, cancel)) { + if ((isPrivate || isUnusedInPkg(info, el, cancel)) && !markedAsUsed.apply(el, declaration)) { result.add(new UnusedDescription(el, declaration, isPkgPrivate, UnusedReason.NOT_USED)); } } @@ -401,6 +417,17 @@ public Void scan(Tree tree, Element p) { return false; } + 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; + } + private enum UseTypes { READ, WRITTEN, USED; } 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..e681ed7b97ea 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 @@ -21,7 +21,6 @@ import com.sun.source.tree.Tree.Kind; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import javax.lang.model.element.ElementKind; import org.netbeans.modules.java.editor.base.semantic.UnusedDetector; import org.netbeans.modules.java.editor.base.semantic.UnusedDetector.UnusedDescription; 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); + } +}