Skip to content

Commit

Permalink
Do not mark test methods as unused.
Browse files Browse the repository at this point in the history
  • Loading branch information
dbalek committed Aug 4, 2023
1 parent f06522f commit 6b8e152
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 14 deletions.
8 changes: 8 additions & 0 deletions java/java.editor.base/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@
<specification-version>1.87</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.spi.java.hints</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>1.56</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.filesystems</code-name-base>
<build-prerequisite/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

/**
*
Expand Down Expand Up @@ -114,6 +118,18 @@ public static List<UnusedDescription> findUnused(CompilationInfo info, Callable<

UnusedVisitor uv = new UnusedVisitor(info);
uv.scan(info.getCompilationUnit(), null);
AtomicReference<List<UsedDetector>> usedDetectors = new AtomicReference<>();
BiFunction<Element, TreePath, Boolean> 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<UnusedDescription> result = new ArrayList<>();
for (Entry<Element, TreePath> e : uv.element2Declaration.entrySet()) {
Element el = e.getKey();
Expand All @@ -124,25 +140,25 @@ public static List<UnusedDescription> 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)) {
if (!isSerialSpecField(info, el) && !lookedUpElement(el, uv.type2LookedUpFields, uv.allStringLiterals)) {
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));
}
}
Expand All @@ -152,13 +168,13 @@ public static List<UnusedDescription> 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));
}
}
Expand Down Expand Up @@ -401,6 +417,17 @@ public Void scan(Tree tree, Element p) {
return false;
}

private static List<UsedDetector> collectUsedDetectors(CompilationInfo info) {
List<UsedDetector> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions java/junit.ui/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@
<specification-version>1.78</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.spi.java.hints</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<specification-version>1.56</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.openide.awt</code-name-base>
<build-prerequisite/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -255,6 +255,31 @@ public List<TestMethod> 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<TestMethod> 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 {

Expand Down
17 changes: 17 additions & 0 deletions java/spi.java.hints/apichanges.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@
<apidef name="JavaHintsSPI">Java Hints SPI</apidef>
</apidefs>
<changes>
<change id="UsedDetector">
<api name="JavaHintsSPI"/>
<summary>Added UsedDetector SPI to mark an arbitrary elements as used</summary>
<version major="1" minor="56"/>
<date day="2" month="8" year="2023"/>
<compatibility addition="yes"/>
<description>
<p>
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.
</p>
</description>
<class package="org.netbeans.spi.java.hints.unused" name="UsedDetector" link="no" />
</change>
<change id="openRefactoringUI">
<api name="JavaHintsSPI"/>
<summary>Added utility methods and SPI to open UI for hints</summary>
Expand Down
2 changes: 1 addition & 1 deletion java/spi.java.hints/nbproject/project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 1 addition & 2 deletions java/spi.java.hints/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,8 @@
</test-dependencies>
<public-packages>
<package>org.netbeans.spi.java.hints</package>
<package>org.netbeans.spi.java.hints.annotations</package>
<package>org.netbeans.spi.java.hints.matching</package>
<package>org.netbeans.spi.java.hints.support</package>
<package>org.netbeans.spi.java.hints.unused</package>
</public-packages>
</data>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit 6b8e152

Please sign in to comment.